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()