Canvas 2D 快速上手

使用Canvas进行2D绘制,需要使用Canvas的上下文对象,上下文对象提供了一些方法(API)来绘制图形。
我们不仅可以通过Canvas绘制图形,还可以通过Canvas绘制动画。

静态绘制

绘制直线

首先,我们需要获取到Canvas的上下文对象,这里的上下文可以理解为画笔。我们需要使用坐标系来让画笔在Canvas上绘制图形,Canvas的坐标系是左上角为原点,向右为x轴,向下为y轴。

1
<canvas id="canvas"></canvas>
1
2
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

然后使用beginPath()方法开始绘制路径,使用moveTo()方法设置路径的起点,使用lineTo()方法设置路径的终点,最后使用stroke()方法绘制路径。
其中strokeStyle是设置路径的颜色。

1
2
3
4
5
ctx.beginPath()
ctx.moveTo(0, 0) // 设置路径的起点
ctx.lineTo(100, 100) // 设置路径的终点
ctx.strokeStyle = 'red' // 设置路径的颜色
ctx.stroke() // 绘制路径

这样,我们就可以在Canvas上绘制一条红色的直线。

绘制折线

我们可以使用lineTo()方法设置多个路径的终点,然后使用stroke()方法绘制路径。

1
2
3
4
5
6
ctx.beginPath()
ctx.moveTo(0, 0) // 设置路径的起点
ctx.lineTo(100, 100) // 设置路径的终点
ctx.lineTo(200, 0) // 设置路径的终点
ctx.strokeStyle = 'red' // 设置路径的颜色
ctx.stroke() // 绘制路径

lineTo()方法可以设置多个路径的终点,然后使用stroke()方法绘制路径。就可以得到一条折线。

绘制多边形

如果我们将路径的起点和终点连接起来,就可以得到一个多边形,也就是让lineTo()方法的终点和起点相同。

1
2
3
4
5
6
7
ctx.beginPath()
ctx.moveTo(0, 0) // 设置路径的起点
ctx.lineTo(100, 100) // 设置路径的终点
ctx.lineTo(200, 0) // 设置路径的终点
ctx.lineTo(0, 0) // 设置路径的终点
ctx.strokeStyle = 'red' // 设置路径的颜色
ctx.stroke() // 绘制路径

这样,我们就可以在Canvas上绘制一个红色边框的三角形。
我们还可以使用fill()方法代替stroke()方法来填充路径,这样就可以得到一个红色填充的三角形。

1
2
3
4
// ctx.strokeStyle = 'red' // 设置路径的颜色
// ctx.stroke() // 绘制路径
ctx.fillStyle = 'red' // 设置路径的颜色
ctx.fill() // 填充路径

如果我们将stroke()方法和fill()方法都使用,那么就会先绘制路径,再填充路径。

假如我们没有将lineTo()方法的终点和起点连接起来,直接使用fill()方法,这种方式也可以得到一个多边形,他会自动将路径的起点和终点连接起来。
我们也可以使用closePath()方法来关闭路径让其自动连接起点和终点,这样也可以得到一个多边形。

1
2
3
4
5
6
7
ctx.beginPath()
ctx.moveTo(0, 0) // 设置路径的起点
ctx.lineTo(100, 100) // 设置路径的终点
ctx.lineTo(200, 0) // 设置路径的终点
ctx.closePath() // 关闭路径
ctx.fillStyle = 'red' // 设置路径的颜色
ctx.fill() // 填充路径

绘制矩形

矩形是Canvas中最常用的图形之一,我们可以使用rect()方法来绘制矩形。

1
2
3
ctx.rect(0, 0, 100, 100) // 设置矩形的起点坐标和宽高
ctx.strokeStyle = 'red' // 设置路径的颜色
ctx.stroke() // 绘制路径

官方也给我们一个更方便的方式来绘制矩形,那就是使用strokeRect()方法和fillRect()方法。

1
2
ctx.strokeRect(0, 0, 100, 100) // 设置矩形的起点坐标和宽高
ctx.fillRect(0, 0, 100, 100) // 设置矩形的起点坐标和宽高

使用strokeRect()方法绘制矩形,会得到一个边框为红色的空心矩形。
使用fillRect()方法绘制矩形,会得到一个填充为红色的实心矩形。

我们还可以使用clearRect()方法来清除矩形,这个方法会清除矩形内的所有内容。

1
2
3
4
ctx.beginPath()
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 200, 200) // 设置矩形的起点坐标和宽高
ctx.clearRect(75, 50, 50, 50) // 设置矩形的起点坐标和宽高

我们可以看到在红色的矩形内有一个空白矩形,这个矩形就是被清除的矩形。clearRect()方法可以理解Canvas画布的橡皮擦,可以清除矩形内的所有内容。

绘制圆弧

我们可以通过arc(x, y, r, startAngle, endAngle, anticlockwise)方法来绘制圆弧,这个方法可以设置圆弧的起点坐标(x, y)、半径(r)、起始角度(startAngle)、结束角度(endAngle)、是否是逆时针(anticlockwise,可选,默认顺时针)。
这里的角度是从x轴正方向开始计算的,顺时针为正,逆时针为负。

1
2
3
4
ctx.beginPath()
ctx.arc(100, 100, 50, 0, Math.PI * 2, false) // 设置圆弧的起点坐标、半径、起始角度、结束角度
ctx.strokeStyle = 'red' // 设置路径的颜色
ctx.stroke() // 绘制路径 也可以使用ctx.fill()来填充路径得到一个实心半圆

这里的角度是弧度。弧度是弧长的度量单位,1弧度等于180度,π等于180度,所以Math.PI 就是一个半圆。这里也可以使用角度来设置圆弧,但是需要将角度转换为弧度,角度转弧度的公式是:弧度 = 角度 * Math.PI / 180。

当人我们也可以在半圆画完之后,继续使用lineTo()方法来绘制路径,这样就可以得到一个不规则的图形。

1
2
3
4
5
6
7
ctx.beginPath()
ctx.arc(100, 100, 50, 0, Math.PI * 2, false) // 设置圆弧的起点坐标、半径、起始角度、结束角度
ctx.strokeStyle = 'red' // 设置路径的颜色
ctx.stroke() // 绘制路径
ctx.lineTo(100, 100) // 设置路径的终点
ctx.strokeStyle = 'blue' // 设置路径的颜色
ctx.stroke() // 绘制路径

贝塞尔曲线

假如我们想画不规则的曲线,可以使用贝塞尔曲线。贝塞尔曲线有三次贝塞尔曲线和二次贝塞尔曲线。

贝塞尔曲线(读作 [bezje])是一种使用数学方法描述的曲线,被广泛用于计算机图形学和动画中。在矢量图中,贝塞尔曲线用于定义可无限放大的光滑曲线。
贝塞尔曲线由至少两个控制点进行描述。Web 技术中使用的是三次贝塞尔曲线,即使用四个控制点 P0、P1、P2 和 P3 描述的曲线。

二次贝赛尔曲线quadraticCurveTo(cp1x, cp1y, x, y),这个方法可以设置二次贝塞尔曲线的控制点坐标(cp1x, cp1y)和终点坐标(x, y)。
三次贝赛尔曲线bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y),这个方法可以设置三次贝塞尔曲线的控制点坐标(cp1x, cp1y)、控制点坐标(cp2x, cp2y)和终点坐标(x, y)。

绘制文字

我们可以使用fillText(text, x, y, maxWidth)方法和strokeText(text, x, y, maxWidth)方法来绘制文字,这个方法可以设置文字内容(text)、文字的起点坐标(x, y)、文字的最大宽度(maxWidth,可选,默认不限制)。

1
2
3
4
5
6
7
ctx.font = '20px Arial' // 设置文字的字体、大小 和css的font属性一样
ctx.textAlign = 'center' // 设置文字的水平对齐方式 可选值:start、end、center、left、right
ctx.textBaseline = 'middle' // 设置文字的垂直对齐方式 可选值:top、hanging、middle、alphabetic、ideographic、bottom
ctx.direction = 'rtl' // 设置文字的方向 可选值:ltr、rtl、inherit
ctx.fillStyle = 'red' // 设置文字的颜色
ctx.fillText('Hello World', 100, 100) // 设置文字的起点坐标
ctx.strokeText('Hello World', 100, 150) // 设置文字的起点坐标

fillText()方法会填充文字,strokeText()方法只会绘制文字的边框。

绘制图片

我们可以使用drawImage(image, dx, dy, dWidth, dHeight)方法来绘制图片,这个方法可以设置图片的起点坐标(dx, dy)、图片的宽度(dWidth)、图片的高度(dHeight)。

首先我们新建一个图片对象,将其的src设置为图片的地址,然后使用drawImage()方法来绘制图片。

1
2
3
4
const img = new Image()
img.src = 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
ctx.drawImage(img, 0, 0, 100, 50) // 设置图片的起点坐标和宽高
ctx.drawImage(img, 0, 0) // 不输入宽高,会使用图片的原始宽高

如果我们的图片过大,在绘制的时候图片还没有加载完成,那么就会绘制失败。
这时候我们可以使用onload事件来监听图片加载完成,然后在绘制图片。

1
2
3
4
img.onload = () => {
ctx.drawImage(img, 0, 0, 100, 50) // 设置图片的起点坐标和宽高
}
img.src = 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'

如果我们只想展示图片的一部分,可以使用drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)方法,这个方法可以设置图片的起点坐标(sx, sy)、图片的宽度(sWidth)、图片的高度(sHeight)、绘制图片的起点坐标(dx, dy)、绘制图片的宽度(dWidth)、绘制图片的高度(dHeight)。
前四个参数是设置图片的起点坐标和宽高,后四个参数是设置绘制图片的起点坐标和宽高也就是在Canvas上的位置和大小。

1
ctx.drawImage(img, 0, 0, 100, 50, 0, 0, 100, 50) // 设置图片的起点坐标和宽高 绘制图片的起点坐标和宽高

样式相关

颜色

在前面我们已经用到过fillStylestrokeStyle这两个属性,这两个属性可以设置填充和描边的颜色。

1
2
ctx.fillStyle = 'red' // 设置填充的颜色
ctx.strokeStyle = 'blue' // 设置描边的颜色

他们的书写方式和CSS的书写方式一样,都是使用颜色名称或者十六进制颜色值。还可以设置渐变,例如:

1
ctx.fillStyle = ctx.createLinearGradient(0, 0, 100, 100) // 设置渐变

我们还可以使用globalAlpha属性来设置透明度,这个属性可以设置全局的透明度,也就是所有绘制的内容都会受到这个属性的影响。

1
ctx.globalAlpha = 0.5 // 设置全局的透明度

默认fillStylestrokeStyle的颜色都是黑色,globalAlpha的透明度是1。如果一个上下文设置过fillStyle或者strokeStyle,那么这个上下文的所有绘制都会使用这个颜色,直到设置新的fillStyle或者strokeStyle

线条样式

除了以上颜色相关的属性,我们还可以设置线条样式,例如:

1
2
3
4
5
ctx.lineWidth = 10 // 设置线条的宽度
ctx.lineCap = 'round' // 设置线条的端点样式 可选值:butt、round、square
ctx.lineJoin = 'round' // 设置线条的连接点样式 可选值:miter、round、bevel
ctx.setLineDash(segments) // 设置线条的虚线样式 参数是一个数组,数组中的每个元素表示虚线的长度
ctx.lineDashOffset = 10 // 设置线条的虚线偏移量

我们通常都给一个线条或者一个路径设置线条样式后,还想给另外一个设置样式,那么我们要重新恢复成默认的话是非常麻烦的。
所以我们可以使用save()restore()方法来保存和恢复线条样式。

1
2
3
4
5
// 线条1及样式...
ctx.save() // 保存线条样式
// 线条2及样式...
ctx.restore() // 恢复线条样式
// 线条3

这里的保存和恢复是栈的结构,所以save()restore()方法可以嵌套使用。
并且他们只存储状态,不存储绘制的内容。

变换

我们可以通过translate(x, y)方法来设置Canvas的坐标系,这个方法可以设置Canvas的起点坐标(x, y)。注意不是移动画板,而是移动Canvas的坐标系。

1
ctx.translate(100, 100) // 设置Canvas的起点坐标

还可以通过rotate(angle)方法来设置Canvas的旋转角度,这个方法可以设置Canvas的旋转角度(angle)。同样,这个方法也是移动Canvas的坐标系。

1
ctx.rotate(Math.PI / 2) // 设置Canvas的旋转角度

ctx.scale(x, y)方法可以设置Canvas的缩放比例,这个方法可以设置Canvas的缩放比例(x, y)。

1
ctx.scale(2, 2) // 设置Canvas的缩放比例

通过上述方法变形了一大堆,如果我们想恢复该怎么操作?

同样,我们可以使用save()restore()方法来保存和恢复变换。

1
2
3
ctx.save() // 保存变换
// 变换...
ctx.restore() // 恢复变换

总结

Canvas的2D绘制API还有很多,这里只是简单的介绍了一下,如果想要了解更多,可以查看Canvas的官方文档。

MDN Canvas 2D 教程

作者

小郑

发布于

2025-01-08

更新于

2025-08-01

许可协议

评论