Canvas是HTML5中一个极为重要的图形绘制元素,它允许开发者通过JavaScript在网页上动态生成像素级图形。无论是绘制简单的矩形、圆形,还是实现复杂的动画效果,Canvas都能胜任。理解Canvas的工作原理,是掌握前端图形开发、数据可视化和网页游戏制作的基础。

Canvas是什么?它和SVG有什么不同?
Canvas由HTML5新增的<canvas>元素提供,它就像一块固定在页面上的透明画布。但画布本身并没有绘图能力,真正负责绘制的是基于JavaScript的Canvas API。通过调用getContext()方法,开发者可以获得一个绘图上下文对象,随后利用该对象提供的各种方法,在画布上逐像素地绘制图形、文字、图像等。
要理解Canvas的价值,可以对比SVG。SVG是一种基于XML的矢量图形描述语言,每个图形都是独立的DOM节点,可以被浏览器单独渲染、绑定事件和修改样式。而Canvas绘制的图形一旦呈现,就会变成画布上的像素,不再保留独立的节点结构。这意味着Canvas很难对绘制后的单个图形做事件监听,但换来的是极高的绘制性能。对于需要频繁重绘的动画、游戏画面,Canvas是更合适的选择;对于需要交互的图标、流程图,SVG则更灵活。
举一个直观的例子:要绘制一万个圆点,SVG会生成一万个DOM节点,页面操作和渲染都会变得迟缓;而Canvas只通过循环调用arc()和fill(),在内存中完成绘制,性能高下立判。这也是Canvas在数据可视化、图像处理等场景中得到广泛应用的根本原因。
绘图基础:获取上下文与理解坐标系
在Canvas上绘图前,必须获取绘图上下文。使用document.getElementById()拿到<canvas>元素,然后调用getContext()方法。最常用的参数是'2d',表示得到一个二维绘图上下文。下面是最基础的一段代码:
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
// 设置画布实际尺寸(绘图缓冲区像素大小)
canvas.width = 600;
canvas.height = 400;
注意,<canvas>元素自带width和height属性,它们决定了画布的实际像素尺寸。如果不设置,默认值是300×150。很多人会误用CSS去设置<canvas>的大小,但CSS属性只改变元素在页面上显示的尺寸,并不会改变画布内部的绘图分辨率。如果CSS尺寸与内部属性不一致,图形会被拉伸或压缩,导致显示模糊。因此,正确的方式是通过JavaScript显式设置width和height。
Canvas的坐标系与数学中的直角坐标系有区别。它以画布左上角为原点,x轴向右为正方向,y轴向下为正方向。相同的参数,放在Canvas中往往会产生上下颠倒的效果。例如,想让一个圆从左上角移动到右下角,y坐标需要逐渐增大,而不是减小。明确这个规则后,后续绘制图形时就不会犯方向性错误。下面这段代码展示了如何通过坐标控制图形位置:
ctx.fillStyle = '#e74c3c'; ctx.fillRect(10, 10, 100, 50); // 左上角区域 ctx.fillStyle = '#2ecc71'; ctx.fillRect(200, 300, 80, 80); // 右下角区域
绘制基本图形:矩形、圆形、线条
Canvas中最容易上手的是矩形绘制。它提供了fillRect()、strokeRect()和clearRect()三个方法,分别用于填充矩形、描边矩形和清除矩形区域。它们都接收四个参数:x坐标、y坐标、宽度、高度。下面是一个完整的示例,展示了三种方法的使用:
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
// 绘制半透明红色填充矩形
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fillRect(20, 20, 150, 100);
// 绘制蓝色描边矩形
ctx.strokeStyle = '#3498db';
ctx.lineWidth = 5;
ctx.strokeRect(200, 20, 150, 100);
// 清除一块区域,形成透明空洞
ctx.clearRect(50, 40, 60, 30);
绘制圆形则要使用路径。路径是Canvas中描述图形轮廓的机制,它不像矩形那样直接绘制,而是先定义轮廓,再通过fill()或stroke()实际显示。绘制圆形的方法是arc(),它接收圆心坐标、半径、起始角度、结束角度和逆时针标志。角度使用弧度制,一周等于2π。参考以下代码:
ctx.beginPath(); ctx.arc(150, 100, 50, 0, Math.PI * 2); ctx.fillStyle = '#f39c12'; ctx.fill();
这里需要注意,绘制每个独立路径之前都要调用beginPath(),否则新路径会与上一次路径粘连到一起。如果希望给圆加边框,可以把fill()换成stroke(),甚至两者同时调用,得到既有填充又有描边的效果。
绘制直线同样依赖路径。最基本的操作是moveTo()移动画笔到起点,lineTo()画线到下一个点,最后调用stroke()将线条渲染出来。示例:
ctx.beginPath(); ctx.moveTo(40, 40); ctx.lineTo(300, 80); ctx.lineTo(200, 200); ctx.strokeStyle = '#2c3e50'; ctx.lineWidth = 3; ctx.stroke();
多段lineTo()可以组成折线或多边形。如果想让路径闭合,可以调用closePath(),它会从当前点画一条直线回到路径起点。闭合并填充后,就能得到一个实心的多边形。
曲线绘制:贝塞尔曲线与文字
贝塞尔曲线是Canvas绘制不规则图形的利器。二次贝塞尔曲线使用quadraticCurveTo(),三次贝塞尔曲线使用bezierCurveTo()。它们通过控制点弯曲路径,让曲线变得平滑自然。这是一个二次贝塞尔曲线的例子:
ctx.beginPath(); ctx.moveTo(30, 150); ctx.quadraticCurveTo(100, 20, 250, 150); ctx.stroke();
在这个例子中,起点是(30,150),终点是(250,150),控制点是(100,20)。曲线会从起点出发,受到控制点的吸引力而向上弯曲,再落回终点。如果想绘制更复杂的波浪形状,可以连续使用多段三次贝塞尔曲线bezierCurveTo(),通过调整两个控制点的位置形成丰富的曲线变化。
除了图形,Canvas还可以直接绘制文字。使用fillText()填充文字,strokeText()描边文字。绘制前需要设置font属性,字面语法与CSS的font属性一致,可以指定大小、字体、粗细等。示例:
ctx.font = 'bold 28px Arial';
ctx.fillStyle = '#34495e';
ctx.fillText('Hello Canvas', 30, 120);
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 1;
ctx.strokeText('Hello Canvas', 30, 200);
文字的位置由textAlign和textBaseline控制,它们分别指定水平对齐和垂直对齐方式。例如设置textAlign = 'center'后,fillText的x参数表示文字中心点的x坐标。灵活使用这些属性,可以精确控制文字在画面中的位置。
状态管理与性能优化
Canvas的绘图状态包括当前填充色、描边色、线宽、字体、变换矩阵等。如果在一个复杂绘图中反复修改这些属性,很容易导致状态混乱。为此,Canvas提供了save()和restore()方法。它们将当前状态压入栈中,或从栈中弹出,实现状态的临时切换和恢复。看下面的例子:
ctx.fillStyle = '#000'; ctx.save(); // 保存当前状态(fillStyle为#000) ctx.fillStyle = '#f00'; ctx.fillRect(0, 0, 50, 50); ctx.restore(); // 恢复之前保存的状态 ctx.fillRect(70, 0, 50, 50); // 此时填充色又变回#000
在动画开发中,性能优化是核心问题。一个常见误区是每一帧都使用setTimeout驱动,导致帧率不稳定。正确做法是使用requestAnimationFrame,它能让浏览器在合适的时间刷新动画,节省CPU资源并且帧率更平滑。下面是一个简单的动画示例:
var x = 0;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(x, 150, 30, 0, Math.PI * 2);
ctx.fillStyle = '#3498db';
ctx.fill();
x += 2;
if (x > canvas.width + 30) x = -30;
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
这段代码中,每一帧都先清除整个画布,再重新绘制圆,让圆从左向右移动。注意clearRect()的参数必须覆盖整个画布尺寸,否则会残留上一帧的痕迹。
针对更大规模的绘制场景,可以使用离屏Canvas技术。先创建一个不显示在页面上的<canvas>,把复杂背景预先绘制好,然后在每一帧动画中只绘制这个背景图像。这样可以大幅减少每帧需要执行的绘图指令,提升渲染效率。无论是制作游戏还是图表动态交互,这个技巧都非常实用。
总结
Canvas是Web前端中不可多得的绘图利器。从本质上讲,它就是一个由JavaScript控制像素的矩形区域。通过getContext('2d')获得上下文后,开发者可以自由绘制矩形、圆形、直线、曲线、文字等基础内容。深入理解坐标系、路径和状态管理,是掌握Canvas的三大关键。同时,在性能敏感的场景下,要合理使用requestAnimationFrame、save()和restore()以及离屏Canvas来优化动画。不断练习这些基础API,你会慢慢发现Canvas能做的事情远超想象,从动态图表到小游戏,它都能为你提供稳定的底层支持。
CanvasJavaScript绘制图形修改时间:2026-08-30 16:09:55