什么是 Canvas
Canvas 是 HTML5 中新增的一种标签,表示一个画布,只是图形容器,绘制功能必须使用js来实现。
js
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>基础的HTML5页面</title>
</head>
<body>
<canvas id="canvas">
这里是canvas标签,当你的浏览器不支持canvas时,会显示这行文字
</canvas>
</body>
</html>
打开后会是一个完全空白的画面,因为canvas本意就是一块画布,画布在html5中是透明的,不可见的。

绘制前的准备
获取 Canvas 对象:
js
const canvas = document.getElementById('canvas');
获取画笔
js
const context = canvas.getContext(contextType, contextAttributes?);
const context = canvas.getContext('2d');
const ctx = canvas.getContext('2d', {
alpha: true, // 启用透明通道(默认就是true)
});
getContext方法用于获取 Canvas 元素的绘图上下文,来对 Canvas 元素进行绘制操作,通常是 2D 类型,用于二维绘图。第一个参数是必须传的,第二个参数代表配置上下文的行为,不同的上下文的配置属性不同。
2d
| 属性 | 说明 | 默认值 |
|---|---|---|
alpha |
是否包含alpha通道(透明度) | true |
colorSpace |
指定渲染上下文的色彩空间(srgb或display-p3) |
srgb |
desynchronized |
是否将画布绘制周期与事件循环解耦以减少延迟 | false |
willReadFrequently |
是否频繁读取像素(用于优化getImageData()性能) |
false |
webgl
| 属性 | 作用 | 说明 |
|---|---|---|
alpha |
是否包含alpha通道 | 和2D一样 |
antialias |
是否开启抗锯齿 | 让图形边缘更平滑 |
depth |
是否包含深度缓冲区 | 用于3D场景的深度检测 |
failIfMajorPerformanceCaveat |
性能低时是否创建上下文 | true:性能差时失败 |
powerPreference |
GPU电源偏好 | default/high-performance/low-power |
premultipliedAlpha |
是否预混合alpha | 用于图像合成 |
preserveDrawingBuffer |
是否保存缓冲区 | true:可以多次读取 |
stencil |
是否包含模版缓冲区 | 用于复杂遮罩效果 |
小结一下:准备工作就分为三步:
- 布置画布:通过添加
<canvas>标签,添加canvas元素 - 获取画布:通过
<canvas>标签的id,获得canvas对象 - 获得画笔:通过canvas对象的
getContext("2d")方法,获得2D环境
绘制线段:
在Canvas中,是基于状态的绘制,所以前面几步都是在确定状态,直到最后一步才会具体绘制。
绘画步骤和现实中画画差不多,可以分为四步:
1. 首先移动画笔至绘画的起始位置
js
context.moveTo(x, y)
// 将笔画移至 x, y 这个位置,canvas中是以画布的左上角为坐标原点,x轴的正方向向右,y轴的正方向向下

2. 确定第一笔的停止点
js
context.lineTo(x, y)
// 从上一个笔的停止点,移动至x,y这里
3. 规划好路线后,选择画笔(粗细,颜色,线条等)
因为 Canvas 是基于状态的,所以我们在选择画笔粗细和颜色的同时,其实也是选择了线条的粗细和颜色。
| 属性 | 说明 | 默认值 | 示例 |
|---|---|---|---|
context.lineWidth |
线条粗细(宽度) ,单位是像素 | 1.0 |
ctx.lineWidth = 5; |
context.strokeStyle |
描边颜色/样式(可为颜色、渐变、图案) | #000000(黑色) |
ctx.strokeStyle = "#AA394C";``ctx.strokeStyle = "red";``ctx.strokeStyle = gradient; |
context.lineCap |
线段末端样式 | "butt" |
"butt"(平头) "round"(圆头) "square"(方头) |
context.lineJoin |
两条线相交处的连接样式 | "miter" |
"miter"(尖角) "round"(圆角) "bevel"(斜角) |
context.miterLimit |
尖角(miter)的最大长度限制(防止过长尖角) | 10 |
ctx.miterLimit = 5; |
4. 进行绘制
确定绘制有两种方法:
js
context.fill() // 填充
context.stroke() // 描边
5. 画一个线条!
js
<body>
<canvas id="myCanvas" style="border: 1px solid black;"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
ctx.moveTo(0, 0);
ctx.lineTo(100, 100);
ctx.lineWidth = 10;
ctx.strokeStyle = 'red';
ctx.stroke();
</script>
</body>

一条红色的线条就画好了。但是我们现在只给 Canvas 设置了边框,那它的宽高是从哪来的呢?
这是浏览器默认给 Canvas 分配的尺寸 300x150 像素的画布。如果我们要自己设置 Canvas 宽高的话有两种方法
- 通过标签内部设置
js
<canvas id="myCanvas" style="border: 1px solid black;" width = "500" height = "500"></canvas>
- 通过 JS 设置
js
const canvas = document.getElementById('myCanvas');
canvas.width = 500;
canvas.height = 500;
这两种方式设置的宽高是等效的
这里有一个特别需要注意的点:我们设置的是画布的物理宽高,也就是 Canvas 元素的真实宽高,并不是通过 Css 设置的样式宽高,如果我们通过 Css 设置了 Canvas 的宽高,效果其实是在物理宽高的基础上进行了等比的缩小或者拉伸
js
<canvas id="myCanvas"
style="border: 1px solid black; width: 100px; height: 100px;" width="500" height="500">
</canvas>

线条组成图形
绘制折线
当我们学会绘制线条后,就可以用线条来组成图形了,方法其实很简单,就是复用上文用过的lineTo() 方法即可:
js
ctx.moveTo(0, 0);
ctx.lineTo(50, 100);
ctx.lineTo(100, 0);
ctx.lineWidth = 10;
ctx.strokeStyle = 'red';
ctx.stroke();

绘制多条折线
如果需要绘制多条不连接的线条,只需在绘制完一次后,再重新移动画笔,重新绘制一次即可
js
ctx.moveTo(0, 0);
ctx.lineTo(50, 100);
ctx.lineTo(100, 0);
ctx.lineWidth = 10;
ctx.strokeStyle = 'red';
ctx.stroke();
ctx.moveTo(100, 0);
ctx.lineTo(150, 100);
ctx.lineTo(200, 0);
ctx.lineWidth = 10;
ctx.strokeStyle = 'blue';
ctx.stroke();
诶?这是不是很奇怪,明明是先红色再蓝色,为什么就全变为蓝色了呢?是因为上文说过的 Canvas 是基于状态绘制的。我们每次使用stroke() 时,它都会把之前设置的状态再次绘制一遍,第一次stroke时会绘制一条红色折线,第二次stroke时,会再次重新绘制之前那条红色的折线,但是画笔已经变为蓝色的了,所以画出的折线全是蓝色的。
为了解决这个问题,我们需要在每次重新绘制时加上beginPath(),这个 API 代表下次绘制的起始处为beginPath()之后的代码。
js
ctx.moveTo(0, 0);
ctx.lineTo(50, 100);
ctx.lineTo(100, 0);
ctx.lineWidth = 10;
ctx.strokeStyle = 'red';
ctx.stroke();
ctx.beginPath(); // 重新定义起始位置
ctx.moveTo(100, 0);
ctx.lineTo(150, 100);
ctx.lineTo(200, 0);
ctx.lineWidth = 10;
ctx.strokeStyle = 'blue';
ctx.stroke();

绘制矩形
我们先使用绘制线条的方式绘制一个矩形
js
// 绘制一个矩形
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(100,50);
ctx.lineTo(100,100);
ctx.lineTo(50,100);
ctx.lineTo(50,50);
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.stroke();
会发现此时最后一笔画时有一个小缺口,这种情况是因为我们设置了lineWidth导致的,如果是默认笔触为1的话是不会有问题的,但是如果笔触越大,线条越宽,这个小缺口就会越明显。此时需要使用closePath()闭合图形。
js
// 绘制一个矩形
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(100,50);
ctx.lineTo(100,100);
ctx.lineTo(50,100);
// ctx.lineTo(50,50); // 最后一笔可以不画
ctx.closePath();

所以我们在绘制图形时需要使用beginPath()和 closePath()包裹起来。
给矩形上色
上文中有提过绘制的两种方法分别是stroke()和fill(),我们需要使用fill()方法给矩形上色。和使用stroke前相同,我们需要先给它设置好属性。
js
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(100,50);
ctx.lineTo(100,100);
ctx.lineTo(50,100);
ctx.closePath();
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.fillStyle = "red";
ctx.fill();
ctx.stroke();

使用rect()方法绘制矩形
由于矩形是常用的图形,所以 Canvas API 封装好了一个定义矩形位置信息的方法,这个方法接受四个参数,分别表示矩形的起点坐标和宽高。
js
ctx.rect(x,y,width,height);
ctx.beginPath();
ctx.rect(50,50,100,100);
ctx.lineWidth = 5;
ctx.strokeStyle = "black";
ctx.fillStyle = "red";
ctx.fill();
ctx.stroke();

今天 Canvas 的学习就到这啦