Canvas绘制视频帧并叠加滤镜的核心是稳定低延迟的“画—读—改—写”闭环,关键在于视频流绑定时设置autoplay、muted、playsinline及显式宽高,等待loadeddata事件后用requestAnimationFrame驱动绘制,滤镜优先用CSS filter,复杂处理需降采样或WebGL加速。

Canvas 绘制视频帧并叠加滤镜效果,核心是把视频流“画进去”,再“读出来、改一改、写回去”。这个闭环必须稳定、低延迟,否则画面卡顿或黑屏。关键不在代码多复杂,而在几个容易被忽略的时机和配置点。
视频流绑定与就绪控制
先用 navigator.mediaDevices.getUserMedia({ video: true }) 获取摄像头权限,拿到 MediaStream 后赋给 <video> 的 srcObject。这一步必须配齐三个属性:
- autoplay:让视频自动开始播放,避免手动触发
- muted:静音,绕过浏览器对自动播放的限制(尤其 iOS 和 Chrome)
- playsinline:防止 iOS 强制全屏,确保 canvas 能正常捕获画面
同时显式设置 width 和 height(如 width="640" height="480"),别只靠 CSS 控制尺寸——否则 canvas 绘制时会因缩放导致像素错位。等 loadeddata 或 canplay 事件触发后再开始绘制,否则前几帧大概率是黑的。
稳定帧采集与绘制节奏
用 requestAnimationFrame 驱动绘制循环,而不是 setInterval。前者与屏幕刷新率同步,不丢帧、不堆帧,更稳。
立即学习“前端免费学习笔记(深入)”;
- 在回调里调用
ctx.drawImage(video, 0, 0, canvas.width, canvas.height) - 若只需处理局部(比如人脸区域),可传入源坐标参数裁剪:
drawImage(video, sx, sy, sw, sh, dx, dy, dw, dh) - 为减轻压力,可加帧率限制:例如每 3 帧处理一次,用计数器或
performance.now()判断时间间隔
像素级滤镜实现与性能优化
滤镜逻辑就是三步:读 → 算 → 写。
-
ctx.getImageData(0, 0, w, h)返回ImageData,其.data是Uint8ClampedArray,按 R-G-B-A 四字节一组排列 - 灰度化推荐加权法:
gray = (r * 0.299 + g * 0.587 + b * 0.114) | 0,位运算比Math.round快 - 反色直接:
data[i] = 255 - data[i](R)、data[i+1] = 255 - data[i+1](G)、data[i+2] = 255 - data[i+2](B) - 提前缓存
data.length,遍历时用i += 4步长,避免循环内重复访问属性或创建对象
大画布(如 1280×720)频繁调用 getImageData/putImageData 容易卡顿。可降采样处理(先缩到 640×480),或用 Web Workers 拆分计算;更重的滤镜建议迁移到 WebGL 加速。
轻量效果优先用 CSS filter
如果只要亮度、对比度、饱和度、模糊这类基础调整,直接给 <video> 设置 CSS filter 更简单高效:
filter: brightness(1.2) contrast(1.1) saturate(0.8) blur(1px);- 用 JS 动态更新:
video.style.filter = newFilterString,注意每次赋值要覆盖完整字符串 - 需要局部增强(如仅人脸),可用容器 +
::before伪元素叠加backdrop-filter实现毛玻璃效果
不复杂但容易忽略



















