Canvas drawImage 捕获 video 黑屏主因是 video 未就绪或跨域污染:需监听 loadeddata 事件、设置 crossOrigin="anonymous"、确保 readyState ≥ 2,且 canvas 宽高为正整数。
直接调用 drawImage 却得到全黑或空白图像,大概率是 video 还没加载完成、没播放、或未满足 canvas 捕获前提。video 必须处于「可绘制状态」:已加载元数据(loadedmetadata)、至少帧已解码(canplay 或 playing),且不能是跨域资源(否则会触发 canvas 污染)。
实操建议:
立即学习“前端免费学习笔记(深入)”;
video 的 loadeddata 事件后再首次调用 drawImage,比 loadedmetadata 更可靠video 元素设置了 crossOrigin="anonymous"(若源为外部 URL),否则即使加载成功也会因污染被拒绝读取autoplay 未生效或用户未交互(如点击)时尝试捕获——部分浏览器会阻止静音 autoplay 下的 canvas 读取video.readyState ≥ 2(HAVE_CURRENT_DATA)再绘制,可用 setInterval 轮询或监听 timeupdate
canvas 绘制本身不自动缩放像素,drawImage 的目标宽高若与 canvas 的 width/height 属性值不一致,就会触发浏览器插值缩放,造成失真。常见错误是只设了 CSS 样式宽高,却没同步设置 canvas 的 DOM 属性。
实操建议:
立即学习“前端免费学习笔记(深入)”;
width 和 height 属性(不是 CSS),例如:canvas.width = 160; canvas.height = 90;
const scale = Math.min(160 / video.videoWidth, 90 / video.videoHeight);,再传入 drawImage(video, 0, 0, video.videoWidth * scale, video.videoHeight * scale)
ctx.scale() 替代尺寸计算——它会影响后续所有绘制,且易叠加误差canvas.toDataURL() 返回空字符串或损坏 base64,通常源于 canvas 被污染(tainted canvas),即曾绘制过跨域但未声明 crossOrigin 的视频/图片;也可能是调用时机过早(video 未就绪)或 canvas 尺寸为 0。
实操建议:
立即学习“前端免费学习笔记(深入)”;
crossOrigin="anonymous",且服务端响应头包含 Access-Control-Allow-Origin: *(或指定域名)video.src 赋值后立刻调用 toDataURL,必须等 loadeddata 或 playing 事件之后width 和 height 是否为正整数——若为 0 或 NaN,toDataURL 必然返回空串canvas.toDataURL('image/png', 1.0);JPEG 可加质量参数(0.1–1.0)控制体积每秒多次 drawImage + toDataURL 会快速堆积 bitmap 数据,尤其在移动端易触发 GC 停顿或 OOM。不是所有场景都需要逐帧截图——缩略图通常只需关键帧或间隔采样。
实操建议:
立即学习“前端免费学习笔记(深入)”;
requestAnimationFrame 替代 setInterval 控制捕获节奏,自然对齐屏幕刷新率if (Date.now() - lastCapture > 500) { capture(); lastCapture = Date.now(); }
toDataURL 后无需清空 canvas,但若后续绘制内容不同,应调用 ctx.clearRect(0, 0, w, h)
canvas.toBlob(callback, type, quality) 避免 base64 字符串内存开销canvas 污染问题最隐蔽也最难调试,一旦发生,整个 canvas 就不可读取——连 getImageData 都会抛错。务必在 video 加载阶段就验证 crossOrigin 设置是否生效,而不是等到截图失败才排查。