5月28日 02:57
如何获取 Canvas 的 2D 上下文并使用基本绘制方法?
获取 Canvas 2D 上下文
通过 canvas.getContext('2d') 获取 2D 渲染上下文,返回 CanvasRenderingContext2D 对象:
javascriptconst canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d');
实际开发中建议做兼容性检查:
javascriptconst canvas = document.querySelector('canvas'); if (!canvas?.getContext) { throw new Error('当前浏览器不支持 Canvas'); } const ctx = canvas.getContext('2d');
注意:同一个 Canvas 元素多次调用 getContext('2d') 返回的是同一个上下文对象,不会重复创建。
基本绘制方法分类
Canvas 2D 的绘制方法可以按用途分为以下几类:
矩形绘制
矩形是 Canvas 中唯一可以直接绘制的图形,不需要路径:
| 方法 | 说明 |
|---|---|
fillRect(x, y, w, h) | 绘制填充矩形 |
strokeRect(x, y, w, h) | 绘制矩形边框 |
clearRect(x, y, w, h) | 清除矩形区域(变为透明) |
路径绘制
所有非矩形图形都需要通过路径来绘制:
javascriptctx.beginPath(); // 开始新路径 ctx.moveTo(50, 50); // 移动画笔到起点 ctx.lineTo(200, 50); // 画直线到 (200, 50) ctx.arc(150, 100, 40, 0, Math.PI * 2); // 画圆弧 ctx.closePath(); // 闭合路径 ctx.fill(); // 填充 ctx.stroke(); // 描边
核心路径方法:
beginPath()— 开始新路径(不会清除已有路径)moveTo(x, y)/lineTo(x, y)— 移动/画直线arc(x, y, r, startAngle, endAngle)— 画圆弧或圆closePath()— 从当前点回到路径起点fill()/stroke()— 填充或描边当前路径
样式设置
绘制前设置样式,影响后续所有绘制操作:
javascriptctx.fillStyle = '#ff6600'; // 填充颜色 ctx.strokeStyle = 'rgba(0,0,255,0.8)'; // 描边颜色 ctx.lineWidth = 3; // 线宽 ctx.lineCap = 'round'; // 线帽样式:butt | round | square ctx.lineJoin = 'miter'; // 连接样式:miter | round | bevel
文本绘制
javascriptctx.font = '24px sans-serif'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('Hello Canvas', 100, 100); // 填充文本 ctx.strokeText('Hello Canvas', 100, 100); // 描边文本
配合 measureText() 可以精确计算文本宽度:
javascriptconst metrics = ctx.measureText('Hello'); console.log(metrics.width); // 文本像素宽度
图像绘制
drawImage() 支持三种调用方式:
javascript// 基础:原尺寸绘制 ctx.drawImage(img, dx, dy); // 缩放:指定目标尺寸 ctx.drawImage(img, dx, dy, dWidth, dHeight); // 裁剪:从源图裁剪区域绘制到目标区域 ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
状态保存与恢复
这是面试高频考点。Canvas 通过栈结构管理绘制状态:
javascriptctx.save(); // 将当前状态(样式、变换等)压入栈 // ... 修改样式、变换 ... ctx.restore(); // 从栈中弹出并恢复最近一次 save 的状态
典型场景:绘制多个不同样式的图形时,用 save/restore 避免样式互相污染。
一个完整示例
把上面的方法组合起来,绘制一个带标题的彩色柱状图:
javascriptconst canvas = document.getElementById('chart'); const ctx = canvas.getContext('2d'); const data = [ { label: 'A', value: 120, color: '#ff6600' }, { label: 'B', value: 80, color: '#0066ff' }, { label: 'C', value: 150, color: '#00cc66' }, ]; const barWidth = 60; const gap = 30; const baseY = 250; data.forEach((item, i) => { const x = 40 + i * (barWidth + gap); const height = item.value; // 绘制柱子 ctx.fillStyle = item.color; ctx.fillRect(x, baseY - height, barWidth, height); // 绘制标签 ctx.save(); ctx.fillStyle = '#333'; ctx.font = '14px sans-serif'; ctx.textAlign = 'center'; ctx.fillText(item.label, x + barWidth / 2, baseY + 20); ctx.fillText(String(item.value), x + barWidth / 2, baseY - height - 8); ctx.restore(); });
常见追问
Q:Canvas 和 SVG 的区别是什么?
Canvas 是像素级绘制,适合高频重绘场景(游戏、图表动画);SVG 是矢量图形,通过 DOM 操作,适合交互式静态图形。Canvas 绘制后无法单独操作某个图形元素,SVG 可以。
Q:如何实现 Canvas 动画?
核心思路是清空画布 + 重绘。用 clearRect 清除上一帧,再绘制新帧,配合 requestAnimationFrame 控制帧率。需要避免在每帧中创建对象,减少 GC 压力。
Q:Canvas 绘制模糊怎么解决?
这是高 DPI 屏幕的常见问题。需要将 Canvas 的实际像素尺寸设为 CSS 尺寸的 devicePixelRatio 倍,再用 ctx.scale(dpr, dpr) 缩放上下文。