Canvas基础知识

563 阅读16分钟

一、canvas 简介

<canvas> 是 HTML5 新增的,一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。

​它最初由苹果内部使用自己 MacOS X WebKit 推出,供应用程序使用像仪表盘的构件和 Safari 浏览器使用。后来,有人通过 Gecko 内核的浏览器 (尤其是 MozillaFirefox),Opera 和 Chrome 和超文本网络应用技术工作组建议为下一代的网络技术使用该元素。

Canvas 是由 HTML 代码配合高度和宽度属性而定义出的可绘制区域。JavaScript 代码可以访问该区域,类似于其他通用的二维 API,通过一套完整的绘图函数来动态生成图形。

​💡 Mozilla 程序从 Gecko 1.8 (Firefox 1.5) 开始支持 <canvas>, Internet Explorer 从 IE9 开始 <canvas> 。Chrome 和 Opera 9+ 也支持 <canvas>

二、Canvas基本使用

2.1 首先新建页面注册canvas

<canvas id="myCanvas"></canvas>

2.2 渲染上下文(Thre Rending Context)

<canvas> 会创建一个固定大小的画布,会公开一个或多个渲染上下文(画笔), 使用渲染上下文来绘制和处理要展示的内容。 ​ 我们重点研究 2D 渲染上下文。

var canvas = document.getElementById('myCanvas');
//获得 2d 上下文对象
var ctx = canvas.getContext('2d');

2.3 代码模板

<template>
  <div>
     <canvas id="myCanvas"></canvas>
  </div>
</template>
<script>
export default {
  data() {
    return {}
  },
  mounted() {
    this.init()
  },
  methods: {
    init() {
      var canvas = document.getElementById('myCanvas')
      var ctx = canvas.getContext('2d')
    },
  }
}
</script>

三、绘制图形

3.1 线条

​上下文对象可以使用
fillRect() 方法绘制矩形、
strokeRect() 方法绘制矩形边框、
beginPath()closePath() 等方法来绘制复杂的路径图形。
​ 还可以设置线条的颜色、宽度,填充图形的颜色等属性

Snipaste_2024-12-06_17-29-14.png

 // 线条
    init() {
      var canvas = document.getElementById('myCanvas')
      var ctx = canvas.getContext('2d')

      ctx.fillRect(50, 50, 100, 50) // 填充矩形fillRect(x,y,width,height)
      ctx.strokeRect(50, 50, 100, 50) // 矩形边框strokeRect(x,y,width,height)

      ctx.beginPath()
      ctx.moveTo(0, 0) //起始点,x1,y1
      ctx.lineTo(0, 100) //终点,x2,y2
      ctx.stroke() //绘制线条

      ctx.beginPath() // begin 分割 重置颜色
      ctx.moveTo(20, 20) //两条直线 全红
      ctx.lineTo(20, 100)
      ctx.stroke()
      ctx.moveTo(40, 20)
      ctx.lineTo(40, 100)
      ctx.strokeStyle = 'red'
      ctx.stroke()

      ctx.beginPath() // begin 分割 重置颜色
      ctx.moveTo(60, 20) //黑
      ctx.lineTo(60, 100)
      ctx.stroke()
      ctx.beginPath() // begin 分割 重置颜色
      ctx.moveTo(80, 20) //红
      ctx.lineTo(80, 100)
      ctx.strokeStyle = 'red'
      ctx.stroke()
    },

3.2 闭合图形

Snipaste_2024-12-06_17-33-15.png

// 闭合图形
    init1() {
      var canvas = document.getElementById('myCanvas1')
      var ctx = canvas.getContext('2d')
      ctx.beginPath() //三角边 不会闭合
      ctx.moveTo(20, 20)
      ctx.lineTo(20, 120)
      ctx.lineTo(100, 120)
      ctx.stroke()

      ctx.beginPath() //三角边 closePath 闭合
      ctx.moveTo(100, 100)
      ctx.lineTo(100, 140)
      ctx.lineTo(140, 140)
      ctx.closePath()
      ctx.stroke()

      ctx.beginPath() //三角边 closePath 闭合
      ctx.moveTo(20, 70)
      ctx.lineTo(20, 100)
      ctx.lineTo(80, 100)
      ctx.closePath()
      ctx.stroke()

      ctx.beginPath() //fill 全闭合 填充
      ctx.moveTo(250, 80) //x,y
      ctx.lineTo(250, 100)
      ctx.lineTo(100, 100)
      ctx.fill()
    },

3.3 圆形

x: 圆弧中心(圆心)的 x 轴坐标。
y: 圆弧中心(圆心)的 y 轴坐标。radius: 圆弧的半径。
startAngle: 圆弧的起始点,x 轴方向开始计算,单位以弧度表示.
endAngle: 圆弧的终点,单位以弧度表示。
anticlockwise : 可选的Boolean值,如果为 true,逆时针绘制圆弧,反之,顺时针绘制。 ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise)

Snipaste_2024-12-06_17-33-19.png

 // 圆形
    init2() {
      var canvas = document.getElementById('myCanvas2')
      var ctx = canvas.getContext('2d')
      ctx.beginPath()
      ctx.arc(75, 75, 50, 0, Math.PI, true) //arc() 函数中表示角的单位是弧度,不是角度。角度与弧度的 js 表达式: 弧度=(Math.PI/180)*角度。
      ctx.stroke()

      ctx.beginPath()
      ctx.arc(150, 75, 25, 0, Math.PI, true) //Math.PI 3.14159 Π 180°
      ctx.stroke()

      ctx.beginPath()
      ctx.arc(195, 75, 20, 0, Math.PI, true)
      ctx.fill()

      ctx.beginPath()
      ctx.arc(55, 75, 20, 0, Math.PI * 2, true)
      ctx.fill()

      ctx.beginPath()
      ctx.arc(195, 130, 20, 0, (3 / 2) * Math.PI, false)
      ctx.fillStyle = 'red'
      ctx.fill()
    },

3.4 贝塞尔曲线

ctx.quadraticCurveTo(cpx, cpy, x, y) cpx: 控制点的 x 轴坐标。 cpy: 控制点的 y 轴坐标。 x: 终点的 x 轴坐标。y:终点的 y 轴坐标。

Snipaste_2024-12-06_17-33-23.png

// 贝塞尔曲线
    init3() {
      var canvas = document.getElementById('myCanvas3')
      var ctx = canvas.getContext('2d')
      // 二次贝塞尔曲线
      ctx.beginPath()
      ctx.moveTo(20, 10)
      ctx.quadraticCurveTo(30, 50, 50, 20)
      ctx.stroke()

      ctx.beginPath()
      ctx.moveTo(50, 50)
      ctx.quadraticCurveTo(100, 80, 80, 20)
      ctx.stroke()

      ctx.beginPath()
      ctx.moveTo(150, 100)
      ctx.quadraticCurveTo(170, 150, 180, 20)
      ctx.fill()

      // 三次贝赛尔曲线
      /**ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
       *cp1x: 第一个控制点的 x 轴坐标。
        cp1y: 第一个控制点的 y 轴坐标。
        cp2x: 第二个控制点的 x 轴坐标。
        cp2y: 第二个控制点的 y 轴坐标。
        x: 结束点的 x 轴坐标。
        y: 结束点的 y 轴坐标。
       */

      ctx.beginPath()
      ctx.moveTo(180, 20)
      ctx.bezierCurveTo(200, 80, 250, 150, 270, 20)
      ctx.stroke()
    },

3.5 绘制文本

fillText: 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。 ctx.fillText(text, x, y, [maxWidth]); text: 使用当前的 font, textAlign, textBaselinedirection 值对文本进行渲染。 x: 文本起点的 x 轴坐标。 y:文本起点的 y 轴坐标。 maxWidth: 可选,绘制的最大宽度。

strokeText: 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。 ctx.strokeText(text, x, y, [maxWidth]); text: 使用当前的 font, textAlign, textBaselinedirection 值对文本进行渲染。 x: 文本起点的 x 轴坐标。 y: 文本起点的 y 轴坐标。 maxWidth: 可选,绘制的最大宽度。

Snipaste_2024-12-06_17-33-27.png

 // 绘制文本
    init4() {
      var canvas = document.getElementById('myCanvas4')
      var ctx = canvas.getContext('2d')
      ctx.fillText('Hell World', 40, 50)
      ctx.fillText('Hell World', 40, 70, 20)

      /**
       * 
       */
      ctx.font = 'bold 20px serif'
      ctx.strokeText('Hello world', 20, 90)

      ctx.font = '12px 宋体' // 规定字号和字体 且必须写在绘制字体之前
      ctx.strokeStyle = 'red' // 规定描边颜色 必须在绘制之前定义
      ctx.fillStyle = 'red' // 规定字体颜色 必须在绘制之前定义
      ctx.textAlign = 'left' // 规定水平对齐方向 可选值:(start, end, left, right or center.)
      ctx.textBaseline = 'top' // 规定垂直对齐方向 可选值:(top, hanging, middle, alphabetic, ideographic, bottom。)
      ctx.direction = 'inherit' // 规定字体方向 可选值:(ltr, rtl, inherit)
      ctx.fillText('Hello World', 20, 20) // ctx.fillText(text, x, y [, maxWidth]);
      ctx.fillText('Hello World', 20, 50) // ctx.fillText(text, x, y [, maxWidth]);
      // 显示的文字 显示的坐标 规定显示的宽度(可选)
      ctx.font = '24px 黑体' // 规定字号和字体 且必须写在绘制字体之前
      ctx.strokeStyle = 'blue' // 规定描边颜色 必须在绘制之前定义
      ctx.fillStyle = 'blue' // 规定字体颜色 必须在绘制之前定义
      ctx.textAlign = 'right' // 规定水平对齐方向 可选值:(start, end, left, right or center.)
      ctx.textBaseline = 'alphabetic' // 规定垂直对齐方向 可选值:(top, hanging, middle, alphabetic, ideographic, bottom。)
      ctx.direction = 'rtl' // 规定字体方向 可选值:(ltr, rtl, inherit)
      ctx.strokeText('你好,世界', 150, 20) // 绘制文字描边,若只有strokeText没有fillText则显示空心字
      ctx.strokeText('你好,世界', 150, 50) // 绘制文字描边,若只有strokeText没有fillText则显示空心字

      var text = ctx.measureText('abcdefg') // 可获取字体的属性,宽度,所在像素位置等
      console.log(text)
      console.log(text.width)
    },

3.6 绘制图片

PNG、GIF 或者 JPEG 使用特定html元素对象或者另一个canvas元素的引用作为源,也可以通过提供一个URL的方式来使用图片使用:
drawImage() 函数将图片绘制到画布上
drawImage(image, x, y) 图片
drawImage(image, x, y, width, height)控制缩放
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 前 4 个是定义图像源的切片位置和大小,后 4 个则是定义切片的目标显示位置和大小。
HTMLImageElement:Image() 函数构造出来的,或者任何的<img>元素
HTMLVideoElement:video标签的视频中抓取当前帧作为一个图像
HTMLCanvasElement: 使用另一个<canvas>元素作为你的图片源。
ImageBitmap: 这是一个高性能的位图,可以低延迟地绘制,它可以从上述的所有源以及其他几种源中生成(通过createImageBitmap方法创建)。

Snipaste_2024-12-06_17-33-31.png

 init5() {
      var canvas = document.getElementById('myCanvas5')
      var ctx = canvas.getContext('2d')
      const imageObj = new Image()
      imageObj.src = 'https://img2.baidu.com/it/u=1499246417,1917417880&fm=253&fmt=auto&app=138&f=JPEG?w=346&h=100'
      imageObj.onload = function() {
        ctx.drawImage(imageObj, 0, 0, 100, 150)
      }
      const imageObj1 = new Image()
      imageObj1.src = 'https://img2.baidu.com/it/u=1499246417,1917417880&fm=253&fmt=auto&app=138&f=JPEG?w=346&h=100'
      imageObj1.onload = function() {
        ctx.drawImage(imageObj, 100, 0, 100, 150)
      }
      const imageObj2 = new Image()
      imageObj2.src = 'https://img2.baidu.com/it/u=1499246417,1917417880&fm=253&fmt=auto&app=138&f=JPEG?w=346&h=100'
      imageObj2.onload = function() {
        ctx.drawImage(imageObj, 200, 0, 50, 50, 250, 50, 100, 50)
      }
    },

3.7 fillStyle 填充 颜色

Snipaste_2024-12-06_17-33-35.png

 init6() {
      var canvas = document.getElementById('myCanvas6')
      var ctx = canvas.getContext('2d')
      for (var i = 0; i < 6; i++) {
        for (var j = 0; j < 6; j++) {
          ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ',' + Math.floor(255 - 42.5 * j) + ',0)'
          ctx.fillRect(j * 50, i * 50, 50, 50)
        }
      }
    },

3.8 strokeStyle 边框 样式

Snipaste_2024-12-06_17-33-38.png

    init7() {
      var canvas = document.getElementById('myCanvas7')
      var ctx = canvas.getContext('2d')
      function randomInt(from, to) {
        return parseInt(Math.random() * (to - from + 1) + from)
      }
      ctx.beginPath()
      for (var i = 0; i < 6; i++) {
        for (var j = 0; j < 6; j++) {
          ctx.strokeStyle = `rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`
          ctx.strokeRect(j * 50, i * 50, 40, 40)
        }
      }
    },

3.9 状态的保存和恢复 save restore

save方法(类似数组的push())。
每一次调用restore方法,上一个保存的状态就从栈中弹出
所有设定都恢复(类似数组的 pop()

绘画状态包括:
当前应用的变形(即移动,旋转和缩放) strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值
当前的裁切路径(clipping path)均可保存

Snipaste_2024-12-06_17-33-41.png

  //
    init8() {
      var canvas = document.getElementById('myCanvas8')
      var ctx = canvas.getContext('2d')

      ctx.fillRect(0, 0, 150, 150) // 使用默认设置绘制一个矩形
      ctx.save() // 保存默认状态

      ctx.fillStyle = 'red' // 在原有配置基础上对颜色做改变
      ctx.fillRect(15, 15, 120, 120) // 使用新的设置绘制一个矩形

      ctx.save() // 保存当前状态
      ctx.fillStyle = '#FFF' // 再次改变颜色配置
      ctx.fillRect(30, 30, 90, 90) // 使用新的配置绘制一个矩形

      ctx.restore() // 重新加载之前的颜色状态 red
      ctx.fillRect(45, 45, 60, 60) // 使用上一次的配置绘制一个矩形

      ctx.restore() // 加载默认颜色配置
      ctx.fillRect(60, 60, 30, 30) // 使用加载的配置绘制一个矩形

      ctx.beginPath()
      ctx.fillRect(150, 0, 150, 150) // 使用默认设置绘制一个矩形
      ctx.save() // 保存默认状态

      ctx.fillStyle = 'blue' // 在原有配置基础上对颜色做改变
      ctx.fillRect(165, 15, 120, 120) // 使用新的设置绘制一个矩形

      ctx.save() // 保存当前状态
      ctx.fillStyle = '#FFF' // 再次改变颜色配置
      ctx.fillRect(180, 30, 90, 90) // 使用新的配置绘制一个矩形

      ctx.restore() // 重新加载之前的颜色状态 blue 倒找1
      ctx.fillRect(195, 45, 60, 60) // 使用加载的配置绘制一个矩形

      ctx.restore() // 加载默认颜色配置 黑色 倒找+1
      ctx.fillRect(210, 60, 30, 30)
    },

3.10 变形、 旋转、放大缩小、变形矩阵

translate translate(x, y)
translate 方法接受两个参数。x 是左右偏移量,y 是上下偏移量
rotate rotate(angle)
旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
scale scale(x, y)
增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大
x , y分别是横轴纵轴的缩放因子,默认 1

transform (变形矩阵) transform(a, b, c, d, e, f)

[a,c,e]
[b,d,f]
[0,0,1]
a (m11): Horizontal scaling. 水平缩放
b (m12): Horizontal skewing. 水平倾斜
c (m21):​ Vertical skewing. 垂直倾斜
d (m22):​ Vertical scaling. 垂直缩放
e (dx):​ Horizontal moving. 水平移动
f (dy):​ Vertical moving. 垂直移动

💡 在做变形之前先保存状态是一个良好的习惯

Snipaste_2024-12-06_17-33-45.png

    init9() {
      var canvas = document.getElementById('myCanvas9')
      var ctx = canvas.getContext('2d')
      ctx.save() //保存坐原点平移之前的状态  0, 0
      ctx.translate(30, 30) // 起始点 定位 30,30
      ctx.strokeRect(0, 0, 50, 50) //绘制边框
      ctx.restore() //恢复到最初状态         0, 0
      ctx.save() //保存坐原点平移之前的状态  0, 0
      ctx.translate(80, 80) // 起始点 定位 80,90
      ctx.fillRect(0, 0, 50, 50) //绘制封闭矩形
      ctx.restore() //恢复到最初状态         0, 0
      ctx.save()
      /**
       * rotate   rotate(angle)
       * 旋转的角度(angle),它是顺时针方向的,以弧度为单位的值。
       */
      ctx.fillStyle = 'red'
      ctx.save() //保存  0,0 red

      ctx.translate(200, 0) //起始点 定位 200,0
      ctx.rotate((Math.PI / 180) * 45) //旋转 Math.PI :3.14159 弧度 = (π / 180) × 角度
      ctx.fillStyle = 'blue' //蓝色
      ctx.fillRect(0, 0, 50, 50) //绘制蓝色 旋转的 封闭矩形
      ctx.restore() //恢复到最初状态   red   0, 0

      ctx.save() //保存  0,0 red
      ctx.translate(200, 0) //起始点 定位 200,0
      ctx.fillRect(0, 0, 50, 50) //红色封闭矩形
      ctx.restore() //重置 0,0 red
      ctx.fillRect(0, 0, 50, 50) //红色封闭矩形

      /**
       * scale   scale(x, y)
       * 增减图形在 canvas 中的像素数目,对形状,位图进行缩小或者放大
       * x,y 分别是横轴和纵轴的缩放因子
       * 默认 1
       */
      ctx.restore() //重置 0,0 默认值 黑色
      ctx.save() //保存  0,0
      ctx.scale(1, 0.5) // x 保持默认 y 缩放0.5
      ctx.fillRect(150, 200, 50, 50) //封闭矩形 150 40 50 25

      /**
       *  transform (变形矩阵)  transform(a, b, c, d, e, f)
       * [a,c,e]
       * [b,d,f]
       * [0,0,1]
       *  a (m11): Horizontal scaling. 水平缩放
          b (m12): Horizontal skewing. 水平倾斜
          c (m21):​ Vertical skewing.   垂直倾斜
          d (m22):​ Vertical scaling.   垂直缩放
          e (dx):​ Horizontal moving.   水平移动
          f (dy):​ Vertical moving.     垂直移动
       */
      ctx.restore() //重置 0,0
      ctx.fillStyle = 'yellow'
      ctx.transform(1, 0, 1, 1, 0, 0)
      ctx.fillRect(120, 100, 40, 40)
    },

3.11 合成

globalCompositeOperation = typetype 是下面 13 种字符串值之一:

  • source-over 这是默认设置,新图像会覆盖在原有图像。
  • source-in 仅仅会出现新图像与原来图像重叠的部分,其他区域都变成透明的。(包括其他的老图像区域也会透明)
  • source-out 仅仅显示新图像与老图像没有重叠的部分,其余部分全部透明。(老图像也不显示)
  • source-atop 新图像仅仅显示与老图像重叠区域。老图像仍然可以显示。
  • destination-over 新图像会在老图像的下面。
  • destination-in 仅仅新老图像重叠部分的老图像被显示,其他区域全部透明。
  • destination-out 仅仅老图像与新图像没有重叠的部分。 注意显示的是老图像的部分区域。
  • destination-atop 老图像仅仅仅仅显示重叠部分,新图像会显示在老图像的下面。
  • lighter 新老图像都显示,但是重叠区域的颜色做加处理。
  • darken 保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的)
  • lighten 保证重叠部分最量的像素。(每个颜色位进行比较,得到最大的)
  • copy 只有新图像会被保留,其余的全部被清除(边透明)。
  • xor 重叠部分会变成透明。

Snipaste_2024-12-06_17-33-49.png

    init10() {
      var canvas = document.getElementById('myCanvas10')
      var ctx = canvas.getContext('2d')

      // 1 'source-over' 这是默认设置,新图像会覆盖在原有图像。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'source-over'
      ctx.fillStyle = 'red'
      ctx.fillRect(25, 25, 50, 50)

      // 2 'source-in' 仅仅会出现新图像与原来图像重叠的部分,其他区域都变成透明的。(包括其他的老图像区域也会透明)
      ctx.fillStyle = 'green'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'source-in'
      ctx.fillStyle = 'yellow'
      ctx.fillRect(25, 25, 50, 50)

      // 3 'source-out' 仅仅显示新图像与老图像没有重叠的部分,其余部分全部透明。(老图像也不显示)
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'source-out'
      ctx.fillStyle = 'black'
      ctx.fillRect(25, 25, 50, 50)

      // 4 source-atop  新图像仅仅显示与老图像重叠区域。老图像仍然可以显示。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'source-atop'
      ctx.fillStyle = 'black'
      ctx.fillRect(25, 25, 50, 50)

      // 5 destination-over   新图像会在老图像的下面。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'destination-over'
      ctx.fillStyle = 'black'
      ctx.fillRect(25, 25, 50, 50)

      // 6 destination-in 仅仅新老图像重叠部分的老图像被显示,其他区域全部透明。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'destination-in'
      ctx.fillStyle = 'black'
      ctx.fillRect(25, 25, 50, 50)

      // 7 destination-out 仅仅老图像与新图像没有重叠的部分。 注意显示的是老图像的部分区域。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'destination-out'
      ctx.fillStyle = 'black'
      ctx.fillRect(25, 25, 50, 50)

      // 8 destination-atop  老图像仅仅仅仅显示重叠部分,新图像会显示在老图像的下面。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'destination-atop'
      ctx.fillStyle = 'black'
      ctx.fillRect(25, 25, 50, 50)

      // 9 lighter  新老图像都显示,但是重叠区域的颜色做加处理。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'lighter'
      ctx.fillStyle = 'red'
      ctx.fillRect(25, 25, 50, 50)

      // 10 darken  保留重叠部分最黑的像素。(每个颜色位进行比较,得到最小的)
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'darken'
      ctx.fillStyle = 'red'
      ctx.fillRect(25, 25, 50, 50)

      // 11 lighten  保证重叠部分最量的像素。(每个颜色位进行比较,得到最大的)
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'lighten'
      ctx.fillStyle = 'red'
      ctx.fillRect(25, 25, 50, 50)

      // 12 copy  只有新图像会被保留,其余的全部被清除(边透明)。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'copy'
      ctx.fillStyle = 'red'
      ctx.fillRect(25, 25, 50, 50)

      // 13 xor  重叠部分会变成透明。
      ctx.fillStyle = 'blue'
      ctx.fillRect(0, 0, 50, 50)

      ctx.globalCompositeOperation = 'xor'
      ctx.fillStyle = 'red'
      ctx.fillRect(25, 25, 50, 50)
    }

3.11 裁剪

裁剪路径 clip()
把已经创建的路径转换成裁剪路径。
裁剪路径的作用是遮罩。只显示裁剪路径内的区域,裁剪路径外的区域会被隐藏。

💡 后可前不可 clip() 只能遮罩在这个方法调用之后绘制的图像,如果是 clip() 方法调用之前绘制的图像,则无法实现遮罩。

Snipaste_2024-12-06_17-33-56.png

    init11() {
      var canvas = document.getElementById('myCanvas11')
      var ctx = canvas.getContext('2d')
      ctx.arc(150, 20, 100, 0, Math.PI * 2) //在之前绘制
      ctx.stroke()

      ctx.fillStyle = 'pink'
      ctx.fillRect(20, 20, 100, 100)

      ctx.arc(150, 20, 100, 0, Math.PI * 2) //复习 画弧度 Π*2
      ctx.clip()
      ctx.fillStyle = 'pink'
      ctx.fillRect(150, 20, 100, 100)
    },

3.12 动画

动画的基本步骤
清空canvas再绘制每一帧动画之前需要清空所有。清空所有最简单的做法就是 clearRect()方法。

保存canvas状态如果在绘制的过程中会更改 canvas 的状态(颜色、移动了坐标原点等),又在绘制每一帧时都是原始状态的话,则最好保存下 canvas 的状态

绘制动画图形这一步才是真正的绘制动画帧

恢复canvas状态如果你前面保存了 canvas 状态,则应该在绘制完成一帧之后恢复 canvas 状态。

控制动画
我们可用通过 canvas 的方法或者自定义的方法把图像会知道到 canvas 上。

正常情况,我们能看到绘制的结果是在脚本执行结束之后。例如,我们不可能在一个 for 循环内部完成动画。 也就是,为了执行动画,我们需要一些可以定时执行重绘的方法。
一般用到下面三个方法: setInterval() setTimeout() requestAnimationFrame()

Snipaste_2024-12-06_17-34-05.png

init12() {
      var canvas = document.getElementById('myCanvas12')
      var ctx = canvas.getContext('2d')
      draw(ctx)
      function draw(ctx) {
        requestAnimationFrame(function step() {
          drawDial(ctx) //绘制表盘
          drawAllHands(ctx) //绘制时分秒针
          requestAnimationFrame(step)
        })
      }
      /*绘制时分秒针*/
      function drawAllHands(ctx) {
        let time = new Date()

        let s = time.getSeconds()
        let m = time.getMinutes()
        let h = time.getHours()

        let pi = Math.PI
        let secondAngle = (pi / 180) * 6 * s //计算出来s针的弧度
        let minuteAngle = (pi / 180) * 6 * m + secondAngle / 60 //计算出来分针的弧度
        let hourAngle = (pi / 180) * 30 * h + minuteAngle / 12 //计算出来时针的弧度

        drawHand(hourAngle, 60, 6, 'red', ctx) //绘制时针
        drawHand(minuteAngle, 106, 4, 'green', ctx) //绘制分针
        drawHand(secondAngle, 129, 2, 'blue', ctx) //绘制秒针
      }
      /*绘制时针、或分针、或秒针
       * 参数1:要绘制的针的角度
       * 参数2:要绘制的针的长度
       * 参数3:要绘制的针的宽度
       * 参数4:要绘制的针的颜色
       * 参数4:ctx
       * */
      function drawHand(angle, len, width, color, ctx) {
        ctx.save()
        ctx.translate(150, 150) //把坐标轴的远点平移到原来的中心
        ctx.rotate(-Math.PI / 2 + angle) //旋转坐标轴。 x轴就是针的角度
        ctx.beginPath()
        ctx.moveTo(-4, 0)
        ctx.lineTo(len, 0) // 沿着x轴绘制针
        ctx.lineWidth = width
        ctx.strokeStyle = color
        ctx.lineCap = 'round'
        ctx.stroke()
        ctx.closePath()
        ctx.restore()
      }

      /*绘制表盘*/
      function drawDial(ctx) {
        let pi = Math.PI

        ctx.clearRect(0, 0, 300, 300) //清除所有内容
        ctx.save()

        ctx.translate(150, 150) //一定坐标原点到原来的中心
        ctx.beginPath()
        ctx.arc(0, 0, 148, 0, 2 * pi) //绘制圆周
        ctx.stroke()
        ctx.closePath()

        for (let i = 0; i < 60; i++) {
          //绘制刻度。
          ctx.save()
          ctx.rotate(-pi / 2 + (i * pi) / 30) //旋转坐标轴。坐标轴x的正方形从 向上开始算起
          ctx.beginPath()
          ctx.moveTo(110, 0)
          ctx.lineTo(140, 0)
          ctx.lineWidth = i % 5 ? 2 : 4
          ctx.strokeStyle = i % 5 ? 'blue' : 'red'
          ctx.stroke()
          ctx.closePath()
          ctx.restore()
        }
        ctx.restore()
      }
    }

到此canvas基础学习结束,道阻且长,行则将至。与诸君共勉。 ⭐️