截图功能实战:拍照按钮、分辨率、倒计时与连拍
拍照截图,听起来好像就是“咔嚓”一下。但真要在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张,超过就自动丢弃最旧的。不然手机浏览器分分钟崩溃给你看。
核心逻辑流程图
避坑指南
做截图功能时,有几个坑我踩过,分享给你:
- 黑屏问题:如果video还没开始播放就截图,canvas画出来是黑的。一定要等
canplay或playing事件触发后再允许截图。 - 跨域问题:如果视频源来自不同域名,canvas的
toDataURL会报安全错误。记得给video加上crossOrigin="anonymous"。 - 内存泄漏:连续拍照时,如果不释放canvas和图片对象,内存会持续增长。建议用
canvas.width = 0来清理,或者复用同一个canvas。 - 格式选择:PNG无损但体积大,JPEG有损但体积小。我一般默认用JPEG,质量设0.9,兼顾画质和大小。
核心要点总结:
- 截图本质是canvas.drawImage + toDataURL
- 分辨率由canvas尺寸决定,与视频分辨率无关
- 倒计时用setInterval实现,注意UI反馈
- 连拍用setInterval控制频率,做好内存管理
好了,截图功能的核心实现就这些。代码量不大,但细节不少。你动手写一遍,遇到问题再回来翻翻这篇文章,应该就能搞定了。