Canvas 实时绘制视频帧本质是持续逐帧抓取 video 元素画面并渲染,需绑定媒体流、设置 autoplay/muted/playsinline、监听 loadeddata 事件、显式设定宽高、用 requestAnimationFrame 稳定绘制、分层管理图层、适配移动端触控与像素比、优化性能避免卡顿。

Canvas 绘制实时视频帧,本质是把 video 元素当前播放的画面“抓取”并“画”到画布上,形成可编程、可叠加、可处理的图像层。它不是简单截图,而是持续逐帧渲染,构成前端视频处理的底层能力。
绑定视频流并确保稳定播放
先用 navigator.mediaDevices.getUserMedia() 获取摄像头流(或加载远程视频),赋给 <video></video> 的 srcObject。关键细节:
- 必须设置
autoplay和muted,否则多数浏览器会阻止自动播放(尤其移动端) - 加上
playsinline属性,避免 iOS 强制全屏干扰绘制 - 等待
loadeddata或canplay事件后再开始绘制,防止捕获黑帧或未解码画面 - 显式设置
video.width和video.height(如640×480),不要依赖 CSS 缩放,否则 Canvas 渲染会失真
用 requestAnimationFrame 稳定逐帧绘制
别用 setInterval,它和屏幕刷新不同步,易丢帧或卡顿。正确做法是:
- 在
requestAnimationFrame回调中调用ctx.drawImage(video, 0, 0, canvas.width, canvas.height) - 判断条件建议用
!video.paused && !video.ended,比单纯监听播放事件更可靠 - 若需控制负载(比如只处理每 2 帧),可用计数器或
performance.now()计算时间间隔 - Canvas 宽高应设为
video.videoWidth/video.videoHeight,保证像素级对齐
分离图层:视频底图 + 可交互涂鸦层
真正实用的融合不是把所有东西画在一个 Canvas 上,而是分层管理:
- 底层 Canvas 专注绘制视频帧,持续运行不中断
- 顶层 Canvas 仅响应鼠标/触控,绘制路径、标注、框选等,
pointer-events: auto - 顶层 Canvas 默认设
pointer-events: none可穿透点击到底层(如做视频区域选择) - 清除涂鸦只需
topCtx.clearRect(0, 0, canvas.width, canvas.height),不影响视频流 - 导出带标注的画面时,用
compositeCtx.drawImage(bottomCanvas, 0, 0); compositeCtx.drawImage(topCanvas, 0, 0)合成
适配移动设备与性能优化
移动端不是“桌面版缩小”,而是要重新考虑输入与渲染:
- 监听
touchstart/touchmove/touchend,而非 mouse 事件 - 坐标换算需考虑
window.devicePixelRatio:获取canvas.getBoundingClientRect(),再用(touch.clientX - rect.left) * devicePixelRatio - 大分辨率(如 1280×720)频繁调用
getImageData易卡顿,可降采样处理或启用 Web Workers 拆分计算 - 滤镜或边缘检测类操作,建议提前缓存
data.length,用位运算(如| 0)替代Math.round提速 - 如性能仍不足,WebGL 是成熟替代方案——用 shader 实现 GPU 加速卷积或色彩变换


















