canvas相关介绍

canvas由Apple首先提出,现在已经有非常好的浏览器支持度。它和<img>标签很像,但是只有widthheight两个属性,在未设置时,为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
2
3
4
5
6
// 样例
ctx.beginPath();
ctx.moveTo(75,50);
ctx.lineTo(100,75);
ctx.lineTo(100,25);
ctx.fill();
所有的路径通过stroke()fill()才能体现效果。

里面包含了常见的moveTolineTo,表示移动画笔/画直线到(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格式的字符串导入为路径。

样式

通过修改fillStylestrokeStyle改变当前填充和描边的默认颜色。支持的颜色格式有

  • 颜色名
  • 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。

同样,通过指定给fillStylestrokeStyle使用。

阴影

  • shadowOffsetX = float
  • shadowOffsetY = float
    shadowOffsetX和shadowOffsetY用来设定阴影在X和Y轴的延伸距离,它们是不受变换矩阵所影响的。负值表示阴影会往上或左延伸,正值则表示会往下或右延伸,它们默认都为0。
  • shadowBlur = float shadowBlur 用于设定阴影的模糊程度,默认为 0。
  • shadowColor = color,默认为黑色

canvas有两种填充规则non-zeroeven-odd,默认为前者。

文字

  • fillText(text, x, y [, maxWidth]) 在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的
  • strokeText(text, x, y [, maxWidth]) 在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的
1
2
ctx.font = "48px PingFangSC";
ctx.strokeText("Hello world", 10, 50);

除了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用法

变形和裁剪

saverestore用来保存和恢复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方法
  • 可选,保存当前状态
  • 使用上面提过的种种方法绘制下一帧
  • 可选,恢复状态

绘制动画通常要结合用户交互以及setTimeoutsetIntervalrequestAnimationFrame

MDN给个一个小球的组合动画可以参考。

像素级操作

ImageData接口描述<canvas>元素的一个包含像素数据的区域。它包含width, height, data单个只读属性。ImageData可以通过ctx.createImageData(width, height)或者从已有对象中创建,除此创建的所有像素都是透明黑。

可以用getImageData(left, top, width, height)方法获取指定范围的ImageData信息,当widthheight都为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()