canvas2d系统和基本绘制

简单地说:初始状态的canvas是一张透明的画布,我们能用不同方法在canvas上画图。

1
<canvas width="1440" height="720"></canvas>

canvas标签的特性只有宽和高可以设置。css中也可以改变它的宽高,但是默认保持原始宽高比。如果在css中强行同时设置宽高的话canvas标签和里面的画面会被拉伸,所以建议使用特性设置宽高。另外,虽然绘制过程中可以作出矢量图,但在浏览器渲染结束以后得到的是位图,这时通过样式放大的图将不会再具有矢量图的特性,马赛克严重。

  • 兼容性:目前canvas基本得到了所有较新版本主流浏览器的支持,详见Can I Use Canvas? ,如果遇到了不兼容的浏览器的话,这里也有解决方案:
1
2
3
4
<canvas width="1440" height="720">
<div>被canvas标签包裹的内容只有当浏览器不支持canvas时会显示,其余时候自动隐藏。</div>
<div>可以使用一张静态图片代替</div>
</canvas>
  • **</canvas>**是必须的:如果没有写结束标签,那么<canvas>之后的所有内容都会被当做替代内容隐藏。
  • canvas也可以用来渲染3d内容,比如使用WebGL

一、使用步骤

  1. 在html中插入canvas标签,设置id(比如tutorial);
  2. 在js中找到canvas标签:const canvas = document.getElementById('tutorial')
  3. 检查支持性:if (!canvas.getContext) return
  4. 获取canvas2d画布:const ctx = canvas.getContext('2d')
  5. 使用canvas自带的方法作画。

二、栅格系统&形状绘制

1. 栅格系统

​ 和绝大部分其他元素一样,默认canvas左上角为原点,向右为x轴正方向,向下为y轴正方向,单位是像素。原点可以用其他方法移动,网格也可以旋转和缩放。

2. 形状绘制

  • 矩形

    一共有三个方法可以对矩形范围内的像素进行操作:

    1. fillRect(x, y, width, height)画一个填充了颜色的矩形,x和y代表矩形左上角,下同;
    2. strokeRect(x, y, width, height)画一个指定矩形的外边框;
    3. clearRect(x, y, width, height)清除指定矩形内所有像素。
  • 路径

    canvas只能画矩形和路径,也就是说除了矩形其他所有形状都要先建立路径再画。路径本身是透明的,必须要描边填充才能在画面上显示出来。

    通过路径画画的步骤:

    1. beginPath方法新建一条路径,相当于把笔拿起来;
    2. moveTo(x, y)方法决定路径起始点(x, y),相当于把笔尖放到点(x, y);路径默认是连续的,想停笔再从新位置开始画就一定要使用moveTo
    3. 用各种方法拖动路径,相当于在画面上规定想画的线;
    4. 如果是用stroke闭合线条,那么需要手动或者用closePath方法闭合路径;fill画闭合填充不需要;
    5. 重复步骤234直到想画的线条/想填充的区域已经规划好了,用stroke()fill()方法描边/填充。

    可以绘制的所有路径如下:

    • 线段:用lineTo(x, y)方法绘制一条从上一个路径的终点/moveTo方法的决定点到(x, y)的路径

    • 弧线:用arc(x, y, r, 开始角度, 结束角度, 是否为逆时针)绘制一条从点(x, y)、以r为半径的弧线。角度使用的是弧度制。

    • 贝赛尔曲线:因为缺少视觉反馈,一般会结合辅助工具一起使用。

      • quadraticCurveTo(cp1x, cp1y, x, y)绘制二次贝赛尔曲线。(cp1x, cp1y)为控制点,(x, y)为结束点;

      • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)绘制三次贝赛尔曲线。(cp1x, cp1y)为控制点1,(cp2x, cp2y)为控制点2,(x, y)为结束点;

    • 矩形:除了直接画矩形以外,也可以先通过rect(x, y, width, height)绘制路径再进行描边/填充。这个方法在绘制前会自动执行moveTo(x, y),最后路径会停在(x, y)。

  • 构造函数Path2D(IE不支持

    • Path2D的实例可以将之前对路径的描述储存为一个路径对象,然后再把它作为strokefill的参数使用描边/填充。这样可以让每个路径独立出来,可读性更强:
    1
    2
    3
    4
    5
    6
    7
    const circle = new Path2D()
    circle.arc(50, 50, 25, 0, Math.PI * 2)
    ctx.stroke(circle)

    const rectangle = new Path2D()
    rectangle.rect(20, 20, 10, 20) // 由于rect自动重置起始点所以不用moveTo
    ctx.fill(rectangle)
    • 可以使用new Path2D(path)克隆一个新的路径实例

    • path1.addPath(path2[, transform])可以将path2添加到path1中(只能添加一个),之后统一进行处理。transform则可以用来进行矩阵变换。

    • 实例化的时候可以用SVG的路径数据来创建路径实例:

      1
      2
      3
      4
      5
      6
      7
      // M20 10:移动到点(20, 10)
      // h 70:水平方向正向移动70
      // v 80:垂直方向正向移动80
      // h -80:垂直方向逆向移动80
      // Z:回到起点处
      var p = new Path2D("M20 10 h 70 v 80 h -80 Z")
      ctx.stroke(p) // 会画出一个斜边在左的直角梯形

三、色彩

  • canvas中可以通过设置strokeStylefillStyle的值来分别给描边和填充上色。注意一旦设置了新颜色,这个颜色就是之后所有描边和填充的默认颜色了。想换颜色就再次设置吧。

  • 设置颜色时要使用css3标准的有效字符串

  • 透明度:

    • 通过改变globalAlpha的值来设置之后所有颜色的透明度,不透明是1.0(默认),完全透明是0.0;
    • 单个描边/填充设置颜色的时候就在赋值的时候使用rgba就好,这个透明度会和globalAlpha的值以乘法叠加。一般只使用这个方法就好。

四、线条

线条有一系列属性可以修改样式:

  • lineWidth = value:设置线条宽度。默认为1.0。

    线宽是指给定路径的中心到两边的粗细。

    换句话说就是在路径的两边各绘制线宽的一半。因为画布的坐标并不和像素直接对应,当需要获得精确的水平或垂直线的时候要特别注意。

  • lineCap = type:设置线条两端的样式;

    有三种样式可以选择:

    • butt(默认)
    • round
    • square

  • lineJoin = type:设置两线段结合处的样式;

    样式有三种:

    • round
    • bevel
    • miter(默认)

  • miterLimit = value:限制当两条线相交时交接处最大长度;

    所谓交接处长度(斜接长度)是指线条交接处内角顶点到外角顶点的长度。

    两线相交时,线段的外侧边缘会被延伸交汇于一点上。线段之间夹角比较大时,交点不会太远,但随着夹角变小,交点距离会呈指数级增大。所以需要限制这个距离,如果交点距离大于设定值,连接效果就会变成 bevel。

  • setLineDash([a, b]):设置当前虚线样式。接受一个数组[a, b]:a表示实线长度,b表示间隔长度。

  • lineDashOffset:设置虚线样式的起始偏移量。动画里可以通过不断改变其大小来实线虚线移动的效果。

  • getLineDash():返回一个包含当前虚线样式,长度为非负偶数的数组。

五、渐变色

使用步骤:

  1. 通过ctx的createLinearGradientcreateRadialGradient方法创建一个渐变对象

    • createLinearGradient(x1, y1, x2, y2)创建一个从(x1, y1)到(x2, y2)的渐变,方向从第一个点到第二个点。
    • createRadialGradient(x1, y1, r1, x2, y2, r2)创建一个从圆(x1, y1, r1)到圆(x2, y2, r2)的渐变。注意第二个圆一定要完整覆盖住第一个圆。第一个圆内完全是范围0的颜色,第二个圆外完全是范围1的颜色。
  2. 使用渐变对象addColorStop方法在自己想要的地方添加基色(范围0.0 ~ 1.0)。

    • 如果两个颜色位置重复了,那么顺序会决定渐变颜色(比如:0处为black、0.5处为white、0.5处为red、1处为blue,那么00.5的地方是黑色到白色渐变,然后0.51的地方是红色到蓝色渐变,白色和红色之间不会有渐变)。
  3. 设置ctx的fillStylestrokeStyle属性的值为渐变对象

  4. 使用各种描边和填充方法图上渐变色。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 创建渐变对象
    const linGrad = ctx.createLinearGradient(0, 0, 10, 20)
    const radGrad = ctx.createRadialGradient(100, 100, 0, 110, 110, 14.15)// 第二个圆的半径要大于10√2

    // 给线性渐变对象添加一个0.1处为'#f91'、0.9处为'#19f'的渐变
    linGrad.addColorStop(.1, '#f91')
    linGrad.addColorStop(.9, '#19f')

    // 给辐射渐变对象添加0处为白色、0.7处为黑色的渐变
    radGrad.addColorStop(0, '#fff')
    radGrad.addColorStop(.7, '#000')

    // 改变笔刷颜色
    ctx.strokeStyle = linGrad
    ctx.fillStyle = radGrad

    // 给需要的地方上色
    ctx.fillRect(90, 90, 130, 130)
    ctx.strokeRect(0, 0, 15, 25)

六、图案-pattern

  • canvas支持用重复图案填充,使用的方法为createPattern(image, type)

    • image:可以是Image对象或者另一个canvas画布对象。使用Image实例时要确定图像已经加载完毕,一般将语句放在load事件内。
    • type:取值为repeatrepeat-xrepeat-yno-repeat
  • 使用方法:

    1. 获取img对象或者其他canvas对象。
    2. 使用ctx.createPattern方法创建一个图案对象。
    3. fillStylestrokeStyle属性赋值为图案对象
    4. 描边或填充

七、阴影

和css中类似,需要设置四个值:

  • shadowOffsetXshadowOffsetY:取值为浮点数,设定阴影延伸距离,正方向分别为→和↓。
  • shadowBlur:取值为浮点数,设定模糊程度。
  • shadowColor:取值为标准css颜色值,默认为rgba(0, 0, 0, 0)

八、填充规则

当用到 fill(或者 clipisPointinPath )时可以选择一个填充规则。该填充规则根据某处在路径的外面或者里面来决定该处是否被填充,这对于自己与自己路径相交或者路径被嵌套的时候是有用的。

1
2
3
4
ctx.beginPath()
ctx.arc(50, 50, 30, 0, Math.PI*2, true)
ctx.arc(50, 50, 15, 0, Math.PI*2, true)
ctx.fill("evenodd")// 这样被填充的是两个路径之间的内容

九、文本

canvas支持绘制文本或者文本的外轮廓:

canvas支持修改文本样式:

  • ctx.font:和css相同,可以修改是否斜体、文字粗细、文字大小、文字字体
  • ctx.textAlign:和css相同
  • ctxtextBaseline:基线对齐选项。可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic
  • ctx.direction:文本方向。可能的值包括:ltr, rtl, inherit。默认值是 inherit

十、图片

canvas中可以使用外链的图片来作图:

  1. 获取图片/其他canvas元素作为图源。
  2. 使用drawImage方法把图片绘制到canvas中。

图源:

  • 同一页面内的图片:如果需要用页面内已经读取的img元素内的图片,canvas可以直接使用其dom对象。
  • 使用其他域名内的图片:通过跨域,canvas可以加载其他域名内的图片。但如果服务器不允许跨域访问,那么有污染canvas的风险。
  • 使用其他canvas元素:drawImage方法支持使用其他canvas的dom对象,该方法常用于缩略图和双缓冲中。
  • 读取新图片:可以使用Image构造函数获取一张新图片。不过需要把回调函数放到load事件中,否则要么什么都不会发生,要么会报错。
  • 使用data:url获取图片:使用base64编码的字符串来定义图片。缺点是图片无法缓存,同时图片太大会导致html文件过大。
  • 使用视频帧截图:canvas支持使用video标签的视频帧,将当前视频播放的画面渲染到canvas上。

绘图:

drawImage方法有三种使用手段:

  • ctx.drawImage(image, x, y):最基础的使用方法,以ctx的点(x, y)为左上角,绘制image图源,大小和图源相同。
  • ctx.drawImage(image, x, y, width, height):可以缩放图源的方法,通过widthheight来控制绘制在canvas上的宽高。
  • ctx.drawImage(image, x0, y0, width0, height0, x, y, width, height):终极方法,可以截取图源image的内容。从以点(x0, y0)为左上角,以width0height0为宽高把需要的内容截取出来,然后绘制在canvas上,绘制方法和第二条一样。常用于精灵图的展示。

是否在图像缩放时平滑处理:

图源在被过度缩放时会产生模糊或像素化,canvas默认会将平滑处理功能打开。根据需要也可以关闭这个功能:

  • ctx.imageSmoothimgEnabled = boolean:默认值为true。