一、canvas 简介
<canvas> 是 HTML5 新增的,一个可以使用脚本(通常为 JavaScript) 在其中绘制图像的 HTML 元素。它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至可以进行实时视频处理和渲染。
它最初由苹果内部使用自己 MacOS X WebKit 推出,供应用程序使用像仪表盘的构件和 Safari 浏览器使用。后来,有人通过 Gecko 内核的浏览器 (尤其是 Mozilla和Firefox),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() 等方法来绘制复杂的路径图形。
还可以设置线条的颜色、宽度,填充图形的颜色等属性
// 线条
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 闭合图形
// 闭合图形
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)
// 圆形
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 轴坐标。
// 贝塞尔曲线
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, textBaseline 和 direction 值对文本进行渲染。
x: 文本起点的 x 轴坐标。
y:文本起点的 y 轴坐标。
maxWidth: 可选,绘制的最大宽度。
strokeText: 在指定的 (x,y) 位置填充指定的文本,绘制的最大宽度是可选的。
ctx.strokeText(text, x, y, [maxWidth]);
text: 使用当前的 font, textAlign, textBaseline 和 direction 值对文本进行渲染。
x: 文本起点的 x 轴坐标。
y: 文本起点的 y 轴坐标。
maxWidth: 可选,绘制的最大宽度。
// 绘制文本
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方法创建)。
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 填充 颜色
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 边框 样式
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)均可保存
//
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. 垂直移动
💡 在做变形之前先保存状态是一个良好的习惯
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重叠部分会变成透明。
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() 方法调用之前绘制的图像,则无法实现遮罩。
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()
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基础学习结束,道阻且长,行则将至。与诸君共勉。 ⭐️