Canvas

297 阅读8分钟

简介

canvas 是H5新增的一个用于图形绘制的标签 参考:Canvas - Web API 接口参考 | MDN (mozilla.org)
例子: 画一个矩形边框和一个填充的矩形

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Canvas Demo</title>
  <style>
    canvas {
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <canvas id="my-canvas" width="600" height="600">
    该浏览器版本不支持canvas标签
  </canvas>
  <script>
  </script>
</body>
</html>
/** @type {HTMLCanvasElement} */  
const canvas = document.getElementById('my-canvas')
// 上下文
const ctx = canvas.getContext('2d')

// 画一个矩形边框
// 设置边框颜色
ctx.strokeStyle = 'red'
// 绘制一个矩形边框
ctx.strokeRect(50, 50, 100, 100)

// 画一个填充的矩形
ctx.fillStyle = 'green'
ctx.fillRect(150, 150, 100, 100)

效果:

image.png

注意: </canvas>不可缺省

绘制形状

canvas 只支持两种形式的图形绘制:矩形和路径(由一系列点连成的线段)

绘制矩形

canvas提供了三种方法绘制矩形

  • fillRect(x, y, width, height):绘制一个填充的矩形
  • strokeRect(x, y, width, height):绘制一个矩形的边框
  • clearRect(x, y, width, height):清除指定矩形区域,让清除部分完全透明。

绘制路径

绘制路径会用到一下方法

  • beginPath():新建一条路径
  • closePath():闭合路径
  • moveTo():设置起点
  • lineTo():绘制直线
  • stroke():通过线条来绘制图形轮廓。
  • fill():通过填充路径的内容区域生成实心的图形

注意:当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合。 例子:画一个三角形

/** @type {HTMLCanvasElement} */  
const canvas = document.getElementById('my-canvas')
// 上下文
const ctx = canvas.getContext('2d')

ctx.beginPath()
ctx.moveTo(50, 50)
ctx.lineTo(100, 100)
ctx.lineTo(50, 150)
ctx.fill()

效果:

image.png

绘制圆弧

绘制圆弧会用到一下方法:

  • arc(x, y, radius, startAngle, endAngle, anticlockwise) 画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
  • arcTo(x1, y1, x2, y2, radius) 根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。
    例子:画一个圆
/** @type {HTMLCanvasElement} */  
const canvas = document.getElementById('my-canvas')
// 上下文
const ctx = canvas.getContext('2d')

// 画一个圆
ctx.arc(100, 100, 50, 0, Math.PI * 2, false)
ctx.fill()

image.png

arc()函数中表示角的单位是弧度,不是角度。角度与弧度的js表达式: 弧度=(Math.PI/180)*角度。

二次贝塞尔曲线及三次贝塞尔曲线

  • quadraticCurveTo(cp1x, cp1y, x, y) 绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点。
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) 绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。

image.png 例子:

/** @type {HTMLCanvasElement} */  
const canvas = document.getElementById('my-canvas')
// 上下文
const ctx = canvas.getContext('2d')

ctx.moveTo(100, 100)
ctx.quadraticCurveTo(125, 50, 200, 75)
ctx.stroke()

效果:

image.png

样式和颜色

色彩

  • fillStyle = color 设置图形的填充颜色。
  • strokeStyle = color 设置图形轮廓的颜色。
  • globalAlpha = transparencyValue 这个属性影响到 canvas 里所有图形的透明度,有效的值范围是 0.0 (完全透明)到 1.0(完全不透明),默认是 1.0。

线型

  • lineWidth = value 设置线条宽度。
  • lineCap = type 设置线条末端样式,type取以下值:
    • butt 线段末端以方形结束,默认值
    • round 线段末端以圆形结束。
    • square 线段末端以方形结束,但是增加了一个宽度和线段相同,高度是线段厚度一半的矩形区域

image.png

  • lineJoin = type 设定线条与线条间接合处的样式,type取以下值:
    • round 通过填充一个额外的,圆心在相连部分末端的扇形,绘制拐角的形状。 圆角的半径是线段的宽度。
    • bevel 在相连部分的末端填充一个额外的以三角形为底的区域, 每个部分都有各自独立的矩形拐角。
    • miter 默认值,通过延伸相连部分的外边缘,使其相交于一点,形成一个额外的菱形区域。这个设置可以通过 miterLimit 属性看到效果。

image.png

  • setLineDash(segments) 设置当前虚线样式。
    segments: 一个Array数组。一组描述交替绘制线段和间距(坐标空间单位)长度的数字。 如果数组元素的数量是奇数, 数组的元素会被复制并重复。例如, [5, 15, 25] 会变成 [5, 15, 25, 5, 15, 25]。
  • lineDashOffset = value 设置虚线样式的起始偏移量。
    • 例子:
      /** @type {HTMLCanvasElement} */
      const canvas = document.getElementById('my-canvas')
      // 上下文
      const ctx = canvas.getContext('2d')
      
      let y = 20
      function drawDashedLine(pattern) {
        ctx.beginPath()
        ctx.setLineDash(pattern)
        ctx.moveTo(0, y)
        ctx.lineTo(300, y)
        ctx.stroke()
        y += 20
      }
      
      drawDashedLine([])
      drawDashedLine([1, 1])
      drawDashedLine([10, 10])
      drawDashedLine([20, 5])
      drawDashedLine([15, 3, 3, 3])
      drawDashedLine([20, 3, 3, 3, 3, 3, 3, 3])
      drawDashedLine([12, 3, 3])  // 等同于 [12, 3, 3, 12, 3, 3]
      
      效果:
      image.png
      • 例子2: 虚线边框矩形
      /** @type {HTMLCanvasElement} */
      const canvas = document.getElementById('my-canvas')
      // 上下文
      const ctx = canvas.getContext('2d')
      
      ctx.setLineDash([4, 2])
      ctx.strokeRect(10, 10, 100, 100)
      
      效果:
      image.png
  • lineDashOffset = value 设置虚线样式的起始偏移量。 例子:蚂蚁线
    /** @type {HTMLCanvasElement} */
     const canvas = document.getElementById('my-canvas')
     // 上下文
     const ctx = canvas.getContext('2d')
    
     var offset = 0
    
     function draw() {
       ctx.clearRect(0, 0, canvas.width, canvas.height)
       ctx.setLineDash([4, 2])
       ctx.lineDashOffset = -offset
       ctx.strokeRect(10, 10, 100, 100)
     }
    
     function march() {
       offset++
       if (offset > 16) {
         offset = 0
       }
       draw()
     }
    
     setInterval(() => {
       march()
     }, 20)
    
    效果:
    11.gif

渐变

  • createLinearGradient(x1, y1, x2, y2):该方法接受 4 个参数,表示渐变的起点 (x1,y1) 与终点 (x2,y2)。 例子:
     /** @type {HTMLCanvasElement} */
      const canvas = document.getElementById('canvas')
      // 上下文
      const ctx = canvas.getContext('2d')
      // 线性渐变
      const linear = ctx.createLinearGradient(100, 100, 100, 300)
      linear.addColorStop(0, 'red')
      linear.addColorStop(0.2, 'orange')
      linear.addColorStop(0.4, 'yellow')
      linear.addColorStop(0.6, 'green')
      linear.addColorStop(0.8, 'blue')
      linear.addColorStop(1, 'purple')
      ctx.fillStyle = linear
      ctx.fillRect(100, 100, 200, 200)
    

image.png

  • createRadialGradient(x1, y1, r1, x2, y2, r2):方法接受 6 个参数,前三个定义一个以 (x1,y1) 为原点,半径为 r1 的圆,后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆。

图案样式

  • createPattern(image, type):该方法接受两个参数。Image 可以是一个 Image 对象的引用,或者另一个 canvas 对象。Type 必须是下面的字符串值之一:repeat,repeat-x,repeat-y 和 no-repeat。 例子:
    /** @type {HTMLCanvasElement} */
     const canvas = document.getElementById('my-canvas')
     // 上下文
     const ctx = canvas.getContext('2d')
    
     // 创建新 image 对象,用作图案
     var img = new Image()
     img.src = 'https://mdn.mozillademos.org/files/222/Canvas_createpattern.png'
     img.onload = function () {
       // 创建图案
       var ptrn = ctx.createPattern(img, 'repeat')
       ctx.fillStyle = ptrn
       ctx.fillRect(0, 0, 150, 150)
     }
    
    image.png

阴影

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

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

  • shadowBlur = float shadowBlur 用于设定阴影的模糊程度,其数值并不跟像素数量挂钩,也不受变换矩阵的影响,默认为 0。

  • shadowColor = color shadowColor 是标准的 CSS 颜色值,用于设定阴影颜色效果,默认是全透明的黑色。

    例子:

    /** @type {HTMLCanvasElement} */
    const canvas = document.getElementById('canvas')
    // 上下文
    const ctx = canvas.getContext('2d')
    
    ctx.shadowOffsetX = 10
    ctx.shadowOffsetY = 10
    ctx.shadowBlur = 3
    ctx.shadowColor = 'red'
    ctx.font = '30px 宋体'
    ctx.fillText('你好你好你好你好', 100, 100)
    

    image.png

绘制文本

文本绘制

canvas 提供了两种方法来渲染文本:

  • fillText(text, x, y [, maxWidth])
    在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的.

  • strokeText(text, x, y [, maxWidth])
    在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的.

    例子:

    /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('my-canvas')
        // 上下文
        const ctx = canvas.getContext('2d')
    
        ctx.font = '30px 宋体'
        ctx.fillText('你好你好你好', 100, 100)
    
        ctx.font = '30px 宋体'
        ctx.strokeText('你好你好你好', 100, 200)
    

image.png

文本的样式

font = value
当前我们用来绘制文本的样式. 这个字符串使用和 CSS font 属性相同的语法. 默认的字体是 10px sans-serif。 textAlign = value
文本对齐选项. 可选的值包括:start, end, left, right, center. 默认值是 starttextBaseline = value
基线对齐选项. 可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabeticdirection = value
文本方向。可能的值包括:ltr, rtl, inherit。默认值是 inherit

使用图像

绘制图像

  • drawImage
   void ctx.drawImage(image, dx, dy);
   void ctx.drawImage(image, dx, dy, dWidth, dHeight);
   void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

image.png

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Canvas Demo</title>
  <style>
    canvas {
      border: 1px solid #ccc
    }
  </style>
</head>
<body>
  <canvas id="my-canvas" width="300" height="300">
    该浏览器版本不支持canvas标签
  </canvas>
  <img src="https://media.prod.mdn.mozit.cloud/attachments/2012/07/09/1456/941aaa8d98ead9a0d46aef80d02198f7/Canvas_sun.png" width="300" height="300" />
  <script>
    /** @type {HTMLCanvasElement} */
    const canvas = document.getElementById('my-canvas')
    // 上下文
    const ctx = canvas.getContext('2d')

    const image = new Image()
    image.src = 'https://media.prod.mdn.mozit.cloud/attachments/2012/07/09/1456/941aaa8d98ead9a0d46aef80d02198f7/Canvas_sun.png'
    image.onload = function() {
      ctx.drawImage(image, 100, 100, 100, 100, 0, 0, 100, 100)
    }
  </script>
</body>

</html>

image.png

变形

状态的保存和恢复

  • save():保存画布(canvas)的所有状态
  • restore():恢复画布状态

移动

  • translate(x, y): 该方法接受两个参数。x 是左右偏移量,y 是上下偏移量,如右图所示。

旋转

  • rotate(angle): 该方法只接受一个参数:旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。

缩放

  • scale(x, y): 该方法可以缩放画布的水平和垂直的单位。两个参数都是实数,可以为负数,x 为水平缩放因子,y 为垂直缩放因子,如果比1小,会缩小图形, 如果比1大会放大图形。默认值为1, 为实际大小。

合成与裁剪

合成

  • globalCompositeOperation = type: 这个属性设定了在画新图形时采用的遮盖策略,其值是一个标识12种遮盖方式的字符串。
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('my-canvas')
// 上下文
const ctx = canvas.getContext('2d')

ctx.fillStyle = 'deeppink'
ctx.arc(100, 100, 50, 0, Math.PI * 2, false)
ctx.fill()
ctx.globalCompositeOperation = 'source-over'
ctx.fillStyle = 'blue'
ctx.fillRect(100, 100, 100, 100)

image.png

  • source-over: 默认值,如上
  • destination-over

image.png

  • source-in

image.png

  • destination-in

image.png

  • source-out

image.png

  • destination-out

image.png

  • source-atop

image.png

  • destination-atop

image.png

  • lighter:两个重叠图形的颜色是通过颜色值相加来确定的。

image.png

  • copy:只显示新图形

image.png

  • xor:图像中,那些重叠和正常绘制之外的其他地方是透明的。

image.png

  • multiply:将顶层像素与底层相应像素相乘,结果是一幅更黑暗的图片。

image.png

  • screen:像素被倒转,相乘,再倒转,结果是一幅更明亮的图片。

image.png

其他的参考:Compositing 示例 - Web API 接口参考 | MDN (mozilla.org)

裁剪路径

  • clip():将当前正在构建的路径转换为当前的裁剪路径。
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('my-canvas')
// 上下文
const ctx = canvas.getContext('2d')

ctx.arc(100, 100, 75, 0, Math.PI * 2, false)
ctx.clip()

ctx.fillRect(0, 0, 100, 100)

image.png