Canvas 竟能这么玩?从画张图到做动画,入门到上瘾只需这篇!

1,792 阅读7分钟

你有没有想过,网页上那些炫酷的游戏、动态数据图表、甚至在线涂鸦板,是怎么实现的?其实它们背后都藏着一个 “神级工具”——HTML5 的<canvas>标签。这个看似简单的画布,能让你用代码画出任何东西,从静态图片到流畅动画,甚至交互式游戏,全凭想象力。

今天我们就从最基础的 “画张图” 开始,一步步解锁 Canvas 的神奇功能。看完这篇,你不仅能看懂示例代码,还能亲手写出会动的效果,感受用代码 “作画” 的乐趣!

认识 Canvas:网页里的 “电子画板”

<canvas>就像一块网页上的 “电子画板”,你可以用 JavaScript 在上面绘制图形、文字、图片,甚至做动画。它的核心优势是直接操作像素,比 CSS 绘图更灵活,能实现复杂的视觉效果。

最基础的用法:创建画布

先看一个最简单的示例(对应你提供的第一个代码):在 Canvas 上画一张图片。

<!-- 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>
屏幕截图 2025-08-12 112716.png

关键知识点

  • <canvas>默认尺寸是 300x150 像素,如需调整大小,用canvas.widthcanvas.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 个核心方法

// 获取画笔
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); // 擦除一块矩形区域,露出画布背景

image.png 效果:红色背景上出现一个 “透明方块”,就像在红纸上挖了个洞。

(2)画文字:动态更新的文本

要实现 “动态数字” 效果,核心是用fillText方法画文字,并通过循环更新内容:

// 初始化一个计数器
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();

20250812-0340-01.2398829.gif 动画原理

  1. requestAnimationFrame:告诉浏览器 “下一次重绘前执行回调”,比setInterval更流畅(不会掉帧);
  2. 每次动画先clearRect清空画布,再重绘文字,就像 “每帧换一张画”,视觉上形成动态效果。

Canvas 动画的核心逻辑:“帧” 的概念

你可能会好奇:为什么这些代码能产生 “动” 的效果?其实动画的本质是 “快速切换静态画面”。

比如电影每秒播放 24 帧(24 张静态图片),人眼就会觉得画面是连续的。Canvas 动画也是如此:

  • requestAnimationFrame每秒执行 60 次回调(60 帧 / 秒);
  • 每次回调都修改绘图参数(位置、颜色、大小等),然后重新绘制;
  • 人眼无法分辨快速切换的画面,就会产生 “动画” 的错觉。

Canvas 能做什么?这些场景超惊艳

掌握了基础绘图和动画后,你可以用 Canvas 实现更酷的功能:

(1)数据可视化:动态图表

比如用 Canvas 画折线图、饼图,支持实时更新数据(比静态图表更灵活)。

(2)网页游戏:交互场景

很多轻量级游戏(如贪吃蛇、俄罗斯方块)都是用 Canvas 开发的,通过监听鼠标 / 键盘事件,实时更新画面。

(3)图片处理:滤镜效果

结合像素操作 API,给图片加滤镜(灰度、模糊、反转色等),比如:

// 获取图片像素数据
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);

image.png

image.png

常见坑:这些错误别再犯

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