canvas相关介绍
canvas由Apple首先提出,现在已经有非常好的浏览器支持度。它和<img>标签很像,但是只有width和height两个属性,在未设置时,为300px和150px。canvas类似<video>和<audio>可以设置替换内容,应对标签本身不被支持的情况。另外,canvas不同于<img>,它必须要有一个</canvas>作为闭合标志。
canvas是一个2D渲染上下文环境(就像webGL是3D渲染上下文环境),在获取到<canvas>元素后,可以通过其getContext方法得到渲染上下文和相关功能,通常传入2d,用来绘制2D图案。
形状
canvas.getContext('2d')得到一个CanvasRenderingContext2D对象。剩下的绘制操作都通过调用对象上的API实现。
canvas的坐标系系统和svg一样,从左上角开始,向右和向下为正,坐标轴单位为像素。下面列出最常见的绘制图形API。
fillRect(x, y, width, height)绘制矩形strokeRect(x, y, width, height)绘制矩形边框clearRect(x, y, width, height)擦除矩形范围
path被用来绘制通用曲线,注意path都是封闭的。绘制路径有4步:
beginPath()新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。- 使用画图命令去画出路径
closePath()闭合路径- 可选
fill()通过填充路径的内容区域生成实心的图形,使用fill()时可以不手动闭合路径 stroke(),为路径添加描边
1 | // 样例 |
里面包含了常见的moveTo和lineTo,表示移动画笔/画直线到(x,y)处。画曲线时可以选择:
arc(x, y, radius, startAngle, endAngle, anticlockwise)
画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。arcTo(x1, y1, x2, y2, radius)根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。quadraticCurveTo(cp1x, cp1y, x, y)绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点。bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。
path2D
通过new path2D()用path2D声明子路径,允许你保留和重用路径对象。除了CanvasRenderingContext2D对象的API外,还可以用addPath新增路径到path2D对象中,它还支持通过SVG格式的字符串导入为路径。
样式
通过修改fillStyle和strokeStyle改变当前填充和描边的默认颜色。支持的颜色格式有
- 颜色名
- hex
- rgb
- rgba
通过globalAlpha修改画布里的所有图形的透明度,取值在0到1之间。
线型样式选择有:
- lineWidth = value 设置线条宽度。
- lineCap = type 设置线条末端样式。默认为butt,还有round、square可选。
- lineJoin = type 设定线条与线条间接合处的样式。round, bevel 和 miter三种可选,miter时,miterLimit可以限制尖角的高度。
- miterLimit = value 限制当两条线相交时交接处最大长度;所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度
设置虚线时,setLineDash(segments)设置当前虚线样式。lineDashOffset = value设置虚线样式的起始偏移量。getLineDash()返回一个包含当前虚线样式,长度为非负偶数的数组。
渐变
渐变需要先指定类型和覆盖范围:
createLinearGradient(x1, y1, x2, y2)方法接受4个参数,表示渐变的起点 (x1,y1) 与终点 (x2,y2)。createRadialGradient(x1, y1, r1, x2, y2, r2)方法接受6个参数,前三个定义一个以 (x1,y1) 为原点,半径为r1的圆,后三个参数则定义另一个以 (x2,y2) 为原点,半径为r2的圆。
创建的渐变通过addColorStop(position, color)添加多个color stop。position参数是一个0.0与1.0之间的数值,表示渐变中颜色所在的相对位置。渐变是一种样式,通过指定给fillStyle或是strokeStyle发挥作用。
模式
通过createPattern(image, type)创建pattern,Image可以是一个Image对象的引用,或者另一个canvas对象。Type描述重复的格式,是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat。
同样,通过指定给fillStyle和strokeStyle使用。
阴影
- shadowOffsetX = float
- shadowOffsetY = float
shadowOffsetX和shadowOffsetY用来设定阴影在X和Y轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为0。 - shadowBlur = float shadowBlur 用于设定阴影的模糊程度,默认为 0。
- shadowColor = color,默认为黑色
canvas有两种填充规则non-zero和even-odd,默认为前者。
文字
fillText(text, x, y [, maxWidth])在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的strokeText(text, x, y [, maxWidth])在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的
1 | ctx.font = "48px PingFangSC"; |
除了font(语法和CSS的font相同)外,还有下面的选择:
- textAlign = value 文本对齐选项. 可选的值包括:start, end, left, right or center. 默认值是 start。
- textBaseline = value 基线对齐选项. 可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic。
- direction = value 文本方向
另外,可以通过measureText(),得到文本绘制的宽度。
使用图片
canvas里可以引入Image对象或其他canvas元素,或者通过URL方式使用图片。
- 使用相同页面内的图片,使用正常地获取元素的方式获取即可
- 使用其它域名下的图片,在HTMLImageElement上使用
crossOrigin属性,可以请求加载其它域名上的图片。若服务器不允许跨域加载,则会污染canvas,即不能导出数据 - 使用canvas,按获取元素的方式获取即可
image可以指定src属性为URL或data:url的形式。甚至引入<video>使用视频帧作为image。绘制图片时,使用:
drawImage(image, x, y)其中image是image或者canvas对象,x和y是其在目标canvas里的起始坐标。drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)是包含了缩放和切片后完整的drawImage用法
变形和裁剪
save和restore用来保存和恢复canvas状态。每当save()方法被调用后,当前的状态就被推送到栈中保存。状态包括所有变形、样式信息。每次restore时会弹出栈顶的状态。建议在做变形和裁剪前保存状态。
变形用到的属性和CSS很像:
translate(x,y)改变当前原点位置,rotate(angle)以当前原点为圆心旋转画布,angle为弧度值。scale(x, y)缩放当前canvas中的图形大小,x和y分别表示两轴上的缩放因子transform(m11, m12, m21, m22, dx, dy)通过变形矩阵进行变换setTransform(m11, m12, m21, m22, dx, dy)先还原为单位矩阵,再按入参的矩阵变换resetTransform重置变形为单位矩阵
globalCompositeOperation定义了图形相互重叠时的处理策略,类似PS中图层的混合模式,默认为darker,还有很多别的选项。
clip()即裁剪方法,和fill以及stroke类似,不过clip将路径对应的部分裁剪出指定区域。
动画
canvas只是一个画布,画出的东西都会保持原样。制作动画只能采取重绘,逐帧绘制,而每一帧包括下面几步:
- 清空canvas,可以使用
clearRect方法 - 可选,保存当前状态
- 使用上面提过的种种方法绘制下一帧
- 可选,恢复状态
绘制动画通常要结合用户交互以及setTimeout,setInterval,requestAnimationFrame。
MDN给个一个小球的组合动画可以参考。
像素级操作
ImageData接口描述<canvas>元素的一个包含像素数据的区域。它包含width, height, data单个只读属性。ImageData可以通过ctx.createImageData(width, height)或者从已有对象中创建,除此创建的所有像素都是透明黑。
可以用getImageData(left, top, width, height)方法获取指定范围的ImageData信息,当width和height都为1时,取得当前像素信息。
使用putImageData(imageData, dx, dy)可以在当前画布(dx, dy)处绘制imageData像素数据。imageSmoothingEnabled默认开启,关闭后可以在图片缩放时看到清楚颗粒化的细节。
导出
主要有三种用法:
canvas.toDataURL('image/png'),默认将canvas导出成png文件canvas.toDataURL('image/jpeg', quality),quality指定在0到1之间,默认为0.92。canvas.toBlob(callback, type, encoderOptions),这个创建了一个在画布中的代表图片的Blob对像
toDataURL除了上面两种导出格式还有别的选择。
交互
<canvas>标签只是一个位图,它并不提供任何已经绘制在上面的对象的信息。canvas的内容不能像语义化的HTML一样暴露给一些协助工具。一般来说,你应该避免在交互型的网站或者App上使用canvas。
addHitRegion(options)可以将添加一个点击区域,options可以参考MDN文档,鼠标事件如果触发在点击区域中,会带上region用于定位点击区域。
另外drawFocusIfNeeded()和scrollPathIntoView()方法还可以绘制焦点圈。
性能问题
- 预渲染相似或重复对象
- 为了避免抗锯齿带来的额外运算,减少使用浮点数
- 在离屏canvas中缓存图片的不同尺寸,不要用
drawImage()去缩放它们 - 使用多层画布去画一个复杂的场景,比如使用多层画布,描绘不同层级的内容。
1
2
3
4
5<div id="stage">
<canvas id="ui-layer" width="480" height="320"></canvas>
<canvas id="game-layer" width="480" height="320"></canvas>
<canvas id="background-layer" width="480" height="320"></canvas>
</div> - 用CSS画大的背景图
- 用CSS transforms特性缩放画布,建议不要将小画布放大,而是去将大画布缩小
- 尽可能避免text rendering
- 尽可能避免 shadowBlur
window.requestAnimationFrame()性能优于window.setInterval()