你有没有想过,网页上那些炫酷的游戏、动态数据图表、甚至在线涂鸦板,是怎么实现的?其实它们背后都藏着一个 "神级工具"------HTML5 的<canvas>
标签。这个看似简单的画布,能让你用代码画出任何东西,从静态图片到流畅动画,甚至交互式游戏,全凭想象力。
今天我们就从最基础的 "画张图" 开始,一步步解锁 Canvas 的神奇功能。看完这篇,你不仅能看懂示例代码,还能亲手写出会动的效果,感受用代码 "作画" 的乐趣!
认识 Canvas:网页里的 "电子画板"
<canvas>
就像一块网页上的 "电子画板",你可以用 JavaScript 在上面绘制图形、文字、图片,甚至做动画。它的核心优势是直接操作像素,比 CSS 绘图更灵活,能实现复杂的视觉效果。
最基础的用法:创建画布
先看一个最简单的示例(对应你提供的第一个代码):在 Canvas 上画一张图片。
html
<!-- 1. 创建画布标签 -->
<canvas id="demo"></canvas>
<script>
// 2. 获取画布元素
let canvas = document.getElementById('demo');
// 3. 获取"画笔"(2D渲染上下文,所有绘图操作都靠它)
let context = canvas.getContext('2d');
// 4. 加载图片并绘制到画布
const img = new Image();
img.src = '图片地址';
// 注意:图片加载是异步的,必须等加载完成再绘制
img.onload = function() {
// 1. 设置 Canvas 尺寸为图片实际尺寸
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
// 绘制图片:参数分别是"图片对象、起点x、起点y"
context.drawImage(img, 0, 0);
};
</script>

关键知识点:
<canvas>
默认尺寸是 300x150 像素,如需调整大小,用canvas.width
和canvas.height
(不要用 CSS,会拉伸变形);getContext('2d')
是核心:返回一个 "2D 绘图上下文" 对象(相当于画笔),所有绘图方法(画矩形、文字、图片等)都定义在这个对象上。
进阶:用代码 "画" 出各种形状
Canvas 的 "画笔"(context
)提供了丰富的绘图方法,能画矩形、圆形、文字等。我们结合示例,看看如何画矩形和动态文字。
类别 | 方法 | 描述 | 参数示例 |
---|---|---|---|
矩形 | fillRect(x, y, width, height) |
绘制填充矩形 | (10, 10, 100, 50) |
strokeRect(x, y, width, height) |
绘制矩形边框 | (10, 70, 100, 50) |
|
clearRect(x, y, width, height) |
清除矩形区域(透明) | (20, 20, 80, 30) |
|
路径 | beginPath() |
开始新路径 | - |
closePath() |
闭合路径(起点终点连线) | - | |
moveTo(x, y) |
移动画笔到点 | (50, 50) |
|
lineTo(x, y) |
添加直线到点 | (150, 50) |
|
arc(x, y, r, sAngle, eAngle, anticlockwise) |
绘制圆弧/圆 x,y :圆心; r :半径; s/eAngle :起止弧度; anticlockwise :逆时针 |
(100, 75, 50, 0, Math.PI*2) |
|
arcTo(x1, y1, x2, y2, r) |
通过控制点绘制圆弧 | (150, 20, 150, 70, 50) |
|
quadraticCurveTo(cpx, cpy, x, y) |
二次贝塞尔曲线 cpx,cpy :控制点; x,y :终点 |
(75, 25, 100, 50) |
|
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) |
三次贝塞尔曲线 cp1* , cp2* : 控制点 |
(70, 20, 130, 20, 100, 50) |
|
rect(x, y, width, height) |
添加矩形路径(需fill() 或stroke() 渲染) |
(10, 10, 100, 50) |
|
fill() / stroke() |
填充/描边路径 | - | |
clip() |
将路径设为裁剪区域 | - | |
文本 | fillText(text, x, y [, maxWidth]) |
填充文本 | ("Hello", 50, 50, 200) |
strokeText(text, x, y [, maxWidth]) |
描边文本 | ("World", 50, 80) |
|
图像 | drawImage(image, dx, dy [, dWidth, dHeight]) |
绘制图像 dx,dy : 目标位置; dWidth,dHeight : 缩放尺寸 |
(img, 10, 10, 100, 100) |
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) |
切片绘制图像 sx,sy : 源图像切片位置; sWidth,sHeight : 切片尺寸 |
(img, 0,0,50,50, 10,10,100,100) |
|
像素操作 | getImageData(sx, sy, sw, sh) |
获取像素数据对象 | (0, 0, canvas.width, canvas.height) |
putImageData(data, dx, dy) |
写入像素数据 | (imgData, 0, 0) |
(1)画矩形:3 个核心方法
javascript
// 获取画笔
let ctx = canvas.getContext('2d');
// 1. 填充矩形:fillRect(x起点, y起点, 宽度, 高度)
ctx.fillStyle = 'red'; // 设置填充颜色
ctx.fillRect(0, 0, 300, 150); // 画一个红色矩形(铺满画布)
// 2. 清空矩形(相当于"橡皮擦"):clearRect(x起点, y起点, 宽度, 高度)
ctx.clearRect(20, 20, 100, 50); // 擦除一块矩形区域,露出画布背景
效果:红色背景上出现一个 "透明方块",就像在红纸上挖了个洞。
(2)画文字:动态更新的文本
要实现 "动态数字" 效果,核心是用fillText
方法画文字,并通过循环更新内容:
javascript
// 初始化一个计数器
let dis = 0;
// 定义动画函数
function animation() {
// 用requestAnimationFrame实现流畅动画(浏览器会自动优化帧率,通常60帧/秒)
requestAnimationFrame(function() {
// 每次动画前先清空画布(避免文字叠加)
ctx.clearRect(0, 0, 300, 150);
// 1. 动态设置文字颜色(用rgba实现渐变色)
ctx.fillStyle = `rgba(${dis}, 0, 0)`; // 红色分量随dis变化
// 2. 设置字体样式
ctx.font = '50px Verdana';
// 3. 画文字:fillText(内容, x起点, y起点)
ctx.fillText(dis, 110, 90); // 显示当前计数器值
// 4. 更新计数器(超过225重置,避免颜色过亮)
dis++;
if (dis >= 225) dis = 0;
// 循环调用,实现持续动画
animation();
});
}
// 启动动画
animation();
动画原理:
requestAnimationFrame
:告诉浏览器 "下一次重绘前执行回调",比setInterval
更流畅(不会掉帧);- 每次动画先
clearRect
清空画布,再重绘文字,就像 "每帧换一张画",视觉上形成动态效果。
Canvas 动画的核心逻辑:"帧" 的概念
你可能会好奇:为什么这些代码能产生 "动" 的效果?其实动画的本质是 "快速切换静态画面"。
比如电影每秒播放 24 帧(24 张静态图片),人眼就会觉得画面是连续的。Canvas 动画也是如此:
- 用
requestAnimationFrame
每秒执行 60 次回调(60 帧 / 秒); - 每次回调都修改绘图参数(位置、颜色、大小等),然后重新绘制;
- 人眼无法分辨快速切换的画面,就会产生 "动画" 的错觉。
Canvas 能做什么?这些场景超惊艳
掌握了基础绘图和动画后,你可以用 Canvas 实现更酷的功能:
(1)数据可视化:动态图表
比如用 Canvas 画折线图、饼图,支持实时更新数据(比静态图表更灵活)。
(2)网页游戏:交互场景
很多轻量级游戏(如贪吃蛇、俄罗斯方块)都是用 Canvas 开发的,通过监听鼠标 / 键盘事件,实时更新画面。
(3)图片处理:滤镜效果
结合像素操作 API,给图片加滤镜(灰度、模糊、反转色等),比如:
javascript
// 获取图片像素数据
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let data = imageData.data;
// 遍历像素,转为灰度图(每个像素的RGB值取平均值)
for (let i = 0; i < data.length; i += 4) {
let avg = (data[i] + data[i+1] + data[i+2]) / 3;
data[i] = avg; // R
data[i+1] = avg; // G
data[i+2] = avg; // B
}
// 把处理后的像素画回画布
ctx.putImageData(imageData, 0, 0);


常见坑:这些错误别再犯
- 用 CSS 设置 Canvas 尺寸 :会导致画面拉伸变形,正确做法是用
canvas.width = 600; canvas.height = 400;
; - 没等图片加载完成就绘制 :图片是异步加载的,必须在
onload
回调里调用drawImage
; - 忘记清空画布 :动画会出现 "拖影",每次重绘前用
clearRect
清空; - 忽略坐标系:Canvas 的原点(0,0)在左上角,y 轴向下为正(和数学坐标系相反)。