截图功能实战:拍照按钮、分辨率、倒计时与连拍

拍照截图,听起来好像就是“咔嚓”一下。但真要在WebRTC里做好,坑不少。

我个人习惯把截图功能拆成四个模块:触发、配置、特效、批量。今天咱们就把这四个模块一个一个啃下来。

1. 拍照按钮功能实现

先搞定最基础的——点击按钮,截一帧画面。

核心思路其实很简单:把video标签当前帧画到canvas上,再转成图片。

// 获取视频流
const video = document.getElementById('localVideo');
const canvas = document.getElementById('snapshotCanvas');
const ctx = canvas.getContext('2d');

// 拍照按钮
document.getElementById('captureBtn').addEventListener('click', () => {
  // 设置canvas尺寸与视频一致
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  
  // 绘制当前帧
  ctx.drawImage(video, 0, 0);
  
  // 转成图片数据
  const imageData = canvas.toDataURL('image/png');
  
  // 显示或下载
  const img = document.createElement('img');
  img.src = imageData;
  document.getElementById('gallery').appendChild(img);
});
小提示:canvas.width和height一定要显式设置。我见过不少新手直接不设,结果画出来是默认的300x150,糊得不行。

这里有个细节:drawImage是同步操作,性能开销很小。但如果你在移动端频繁截图,建议用requestAnimationFrame做节流。

2. 设置截图分辨率

很多人以为截图分辨率就是视频分辨率。其实不是。

你可以截出比视频更高清的画面,也可以截缩略图。关键就在canvas的尺寸上。

function captureWithResolution(width, height) {
  canvas.width = width;
  canvas.height = height;
  
  // 绘制时自动缩放
  ctx.drawImage(video, 0, 0, width, height);
  
  return canvas.toDataURL('image/jpeg', 0.92);
}

// 使用示例
const hdSnapshot = captureWithResolution(1920, 1080);
const thumbnail = captureWithResolution(320, 180);
分辨率预设 宽高比 适用场景
640x480 4:3 快速预览、缩略图
1280x720 16:9 标准高清截图
1920x1080 16:9 全高清输出
原始尺寸 自动 保留视频原始画质
注意:分辨率设得越高,生成的图片越大。我曾经在项目中把截图设成4K,结果一张图8MB,上传时直接把服务器搞崩了。建议给用户提供分辨率选择,默认用720p。

3. 添加倒计时拍照效果

倒计时拍照,说白了就是延迟执行截图。但用户体验要做好,得有视觉反馈。

function countdownCapture(seconds = 3) {
  const overlay = document.getElementById('countdownOverlay');
  let count = seconds;
  
  overlay.textContent = count;
  overlay.style.display = 'flex';
  
  const timer = setInterval(() => {
    count--;
    overlay.textContent = count;
    
    if (count <= 0) {
      clearInterval(timer);
      overlay.style.display = 'none';
      // 执行截图
      doCapture();
    }
  }, 1000);
}

嗯,这里要注意:倒计时期间不能让用户再次点击拍照按钮。我习惯在倒计时开始时禁用按钮,结束后再启用。

视觉反馈建议:
  • 数字居中放大显示,带淡入淡出动画
  • 最后1秒时数字变红,增加紧迫感
  • 截图瞬间加一个闪光效果(白色遮罩闪一下)

4. 连续拍照与连拍功能

连拍和连续拍照,其实是两个东西。

  • 连续拍照:用户手动点一次拍一张,但可以快速连续点击
  • 连拍:按住按钮,自动以固定间隔连续拍摄多张

先看连拍的实现:

let burstInterval = null;
const BURST_INTERVAL = 200; // 200ms一张,每秒5张

function startBurst() {
  if (burstInterval) return;
  
  burstInterval = setInterval(() => {
    const snapshot = captureWithResolution(1280, 720);
    addToGallery(snapshot);
  }, BURST_INTERVAL);
}

function stopBurst() {
  if (burstInterval) {
    clearInterval(burstInterval);
    burstInterval = null;
  }
}

// 按钮事件
burstBtn.addEventListener('mousedown', startBurst);
burstBtn.addEventListener('mouseup', stopBurst);
burstBtn.addEventListener('mouseleave', stopBurst);

连续拍照就简单了,每次点击都执行截图,但要注意防抖:

let lastCaptureTime = 0;
const MIN_INTERVAL = 100; // 最小间隔100ms

document.getElementById('captureBtn').addEventListener('click', () => {
  const now = Date.now();
  if (now - lastCaptureTime < MIN_INTERVAL) return;
  
  lastCaptureTime = now;
  doCapture();
});
经验之谈:连拍时图片会快速堆积。我建议在内存中只保留最近20张,超过就自动丢弃最旧的。不然手机浏览器分分钟崩溃给你看。

核心逻辑流程图

截图功能核心流程 用户触发拍照 判断拍照模式 单拍:立即截图 倒计时:延迟截图 连拍:定时截图 Canvas绘制帧 输出图片/存入相册

避坑指南

做截图功能时,有几个坑我踩过,分享给你:

  • 黑屏问题:如果video还没开始播放就截图,canvas画出来是黑的。一定要等canplayplaying事件触发后再允许截图。
  • 跨域问题:如果视频源来自不同域名,canvas的toDataURL会报安全错误。记得给video加上crossOrigin="anonymous"
  • 内存泄漏:连续拍照时,如果不释放canvas和图片对象,内存会持续增长。建议用canvas.width = 0来清理,或者复用同一个canvas。
  • 格式选择:PNG无损但体积大,JPEG有损但体积小。我一般默认用JPEG,质量设0.9,兼顾画质和大小。
核心要点总结:
  1. 截图本质是canvas.drawImage + toDataURL
  2. 分辨率由canvas尺寸决定,与视频分辨率无关
  3. 倒计时用setInterval实现,注意UI反馈
  4. 连拍用setInterval控制频率,做好内存管理

好了,截图功能的核心实现就这些。代码量不大,但细节不少。你动手写一遍,遇到问题再回来翻翻这篇文章,应该就能搞定了。

公众号:蓝海资料掘金营,微信deep3321