Canvas绘图需理解路径概念:beginPath()开始,moveTo()/lineTo()/arc()构建路径,stroke()/fill()渲染;动画用requestAnimationFrame并注意清屏、对象复用和坐标换算。

canvas 绘制矩形、圆形、线条这些基础图形怎么写
Canvas 本身不提供“画一个圆”这种高级命令,所有图形都靠 beginPath() + moveTo() + lineTo() / arc() + stroke() 或 fill() 组合完成。关键不是记住 API,而是理解“路径”这个概念——它像一支虚拟的笔,你指挥它移动、转弯、落笔、提笔,最后才决定描边还是填充。
常见错误:调用 fill() 后再调用 stroke(),结果只看到填充色——因为默认填充会盖住描边;正确做法是先 stroke() 再 fill(),或用 closePath() 显式闭合路径避免意外重叠。
- 画实心矩形:
ctx.fillRect(x, y, width, height)(无需路径操作) - 画空心圆:
ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI * 2); ctx.stroke(); - 画折线:
ctx.beginPath(); ctx.moveTo(10,10); ctx.lineTo(50,20); ctx.lineTo(30,60); ctx.stroke();
让图形动起来:requestAnimationFrame 是唯一靠谱的选择
别用 setTimeout 或 setInterval 驱动 canvas 动画——它们不和屏幕刷新同步,容易掉帧、卡顿、耗电。浏览器原生的 requestAnimationFrame() 才是标准方案,它会在下一次重绘前自动触发回调,且在标签页不可见时自动暂停。
动画循环的核心结构固定:
立即学习“前端免费学习笔记(深入)”;
function animate() {
// 1. 清除上一帧(常用 clearRect 或重绘背景)
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 2. 更新状态(比如 x += dx)
ball.x += ball.dx;
// 3. 绘制新帧
ctx.beginPath();
ctx.arc(ball.x, ball.y, 10, 0, Math.PI * 2);
ctx.fill();
// 4. 请求下一帧
requestAnimationFrame(animate);
}
animate();注意:clearRect() 不清空整个 canvas 元素 DOM,只是擦除像素;如果 canvas 有 CSS 缩放,要按实际渲染尺寸清除,否则会出现残影。
canvas 动画卡顿?先检查这三件事
90% 的卡顿不是性能问题,而是逻辑或调用方式出错。
- 没清屏或清屏范围不对:比如只清了局部区域,旧图形残留形成拖影
- 每帧重复创建路径对象或 Image 对象:把
new Image()或ctx.createLinearGradient()放在动画循环外初始化 - 在
drawImage()中传入未加载完成的图片:会静默失败,画面空白——务必监听img.onload再开始动画
另外,canvas.width 和 canvas.height 是 canvas 元素的“位图分辨率”,修改它们会清空画布并重置所有上下文状态;而 canvas.style.width 只影响缩放显示,别混用。
想响应鼠标/触摸事件?坐标得自己换算
Canvas 没有内置的“点击某个图形”的事件机制。所有交互都要靠监听 canvas 元素上的 mousemove、click 等事件,再把屏幕坐标转成 canvas 坐标,最后手动判断是否落在某个图形内。
换算公式很简单:canvasX = (event.offsetX || event.layerX) - canvas.getBoundingClientRect().left,但要注意:如果 canvas 用了 CSS transform 或设置了 border,就得用 getBoundingClientRect() 精确计算偏移。
判断点是否在圆内:Math.hypot(x - centerX, y - centerY) ;判断点是否在矩形内:x > rectX && x rectY && y 。别依赖 isPointInPath() 做实时检测——它只对最后一次路径有效,且性能不如直接数学计算。
真正难的从来不是画什么,而是怎么让画出来的东西“知道”自己被点了、拖了、缩放了——这些逻辑全得自己补。










