Canvas绘图需先用getContext('2d')获取上下文,否则绘图方法报错;动画必须用requestAnimationFrame并手动清空画布;鼠标坐标须用getBoundingClientRect校准;逐像素操作应复用createImageData提升性能。

JavaScript 与 Canvas 的交互不是“连接”而是“获取上下文后直接绘图”,没有中间层或事件代理——Canvas 是立即模式(immediate mode)的位图画布,所有绘制调用都实时生效,且不保留图形对象。
如何正确获取 2d 上下文并开始绘图
必须先用 getContext('2d') 获取渲染上下文,否则所有绘图方法(如 fillRect、strokeLine)都会报 TypeError: Cannot read property 'fillRect' of null。注意:getContext 返回 null 的常见原因包括:Canvas 元素尚未加载完成、传入了错误的上下文类型(如 'webgl2' 但浏览器不支持)、或 Canvas 尺寸为 0。
- 确保 DOM 加载完成后再执行获取逻辑,例如放在
window.addEventListener('load', ...)或document.querySelector('canvas').getContext('2d')前加存在性判断 - 不要依赖 CSS 设置 Canvas 大小:CSS 缩放会拉伸像素,导致模糊;应直接设置
canvas.width和canvas.height属性 -
getContext('2d')只需调用一次,重复调用无害但没必要
requestAnimationFrame 为什么是动画的唯一合理选择
Canvas 动画不是靠 setInterval 驱动的——它无法对齐屏幕刷新率,容易掉帧、卡顿,且在标签页后台时仍运行,浪费资源。而 requestAnimationFrame 自动暂停、匹配刷新率、提供高精度时间戳,是 Canvas 动画的事实标准入口。
- 每次回调中必须清空画布(
clearRect)或重绘全部内容;Canvas 不自动保存场景,不手动清理就会残留上一帧 - 避免在回调里做耗时计算(如大量路径生成、JSON 解析),否则会阻塞渲染帧
- 时间戳参数可用于计算 delta 时间,实现帧率无关的运动逻辑,例如:
const delta = (timestamp - lastTime) / 1000;
鼠标/触摸事件坐标如何映射到 Canvas 像素坐标
Canvas 坐标系原点在左上角,但 clientX/clientY 是相对于视口的,且受 CSS 缩放、滚动、边框影响。直接使用会导致点击位置偏移,尤其在响应式布局中。
立即学习“Java免费学习笔记(深入)”;
- 用
getBoundingClientRect()获取 Canvas 相对于视口的实际位置,再减去偏移:const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; - 如果 Canvas 被 CSS 缩放(如
transform: scale(1.5)),需额外除以rect.width / canvas.width校正 - 触摸事件需遍历
e.touches,每个Touch对象同样要过getBoundingClientRect校准
性能关键:何时该用 createImageData + putImageData
逐像素操作(如图像滤镜、粒子系统、噪声生成)若用 fillRect 或 arc 画成千上万个点,性能会断崖式下降。此时应绕过绘图 API,直接操作像素数组。
-
createImageData(width, height)分配离屏像素缓冲,putImageData()一次性写入,比单点绘制快 10–100 倍 - 修改
data数组时注意:每个像素占 4 字节(RGBA),索引为(y * width + x) * 4,顺序是r, g, b, a - 不要在每帧都调用
createImageData——复用同一实例,否则触发频繁内存分配
Canvas 的“交互”本质是状态管理:你控制何时清空、何时重绘、如何响应输入、怎样组织数据更新。最易被忽略的是——它不维护任何图形对象,所有“图形”只是像素,删不掉、改不了,只能重画。











