实现多路标注截图
1. 准备截图环境
- 创建合成画布:新建一个与原始参考尺寸(
referenceWidth
和referenceHeight
)一致的临时画布,用于合成背景、视频内容和标注信息。 - 获取有效视频元素:筛选页面中已加载的视频元素(
readyState >= 2
且尺寸有效),确保截图包含实时视频内容。
2. 绘制基础内容
- 填充背景:在合成画布上用白色填充整个区域,作为截图的基础背景。
- 绘制视频内容:
- 计算视频元素在标注容器中的相对位置和尺寸。
- 根据相对比例将视频绘制到合成画布的对应位置,保证视频内容与标注的空间对应关系。
3. 绘制标注信息
- 遍历标注历史:将
drawingHistory
中存储的所有标注动作(包括手绘、图形、文字、马赛克等)绘制到合成画布。 - 处理当前绘制内容:如果存在正在绘制但未完成的动作(
currentAction
),同样将其绘制到画布,确保截图完整性。 - 按工具类型绘制:针对不同标注工具(如马赛克、箭头、文字等),调用对应的绘制方法,将百分比坐标转换为合成画布的实际像素坐标后绘制。
4. 导出与下载截图
- 生成图片数据:通过
canvas.toDataURL('image/png')
将合成画布内容转换为 PNG 格式的图片数据 URL。 - 触发下载:
- 创建临时
<a>
标签,设置download
属性指定文件名(含时间戳确保唯一性)。 - 将图片数据 URL 赋值给
<a>
标签的href
属性,模拟点击实现下载。 - 清理临时元素和 URL,释放内存。
- 创建临时
5. 异常处理与反馈
- 错误捕获:使用
try-catch
捕获截图过程中的异常(如 canvas 操作失败),并通过ElMessage
提示用户。 - 成功反馈:截图成功后,通过
ElMessage
显示成功提示,提升用户体验。
步骤 1:触发截图功能
用户点击 “截图” 按钮时,调用takeScreenshot
方法启动截图流程。
对应代码:
步骤 2:获取有效视频元素
筛选页面中已加载的视频元素(排除未就绪或无尺寸的视频),确保截图包含实时视频内容。
对应代码:
步骤 3:创建合成画布
新建与原始参考尺寸(referenceWidth
和referenceHeight
)一致的临时画布,用于合成背景、视频和标注。
对应代码:
步骤 4:绘制基础背景
在合成画布上填充白色背景,作为截图的底层。
对应代码:
步骤 5:绘制视频内容
计算视频在标注容器中的相对位置,按比例绘制到合成画布,确保视频与标注位置对应。
对应代码:
步骤 6:绘制标注内容
将历史标注和当前正在绘制的内容(如手绘、图形、马赛克等)绘制到合成画布。
对应代码:
// 绘制历史标注
drawingHistory.value.forEach(action => {drawActionToCanvas(action, ctx, compositeCanvas);
});
// 绘制当前正在绘制的内容(未完成的标注)
if (currentAction) {drawActionToCanvas(currentAction, ctx, compositeCanvas);
}// 绘制单个标注到合成画布的方法
const drawActionToCanvas = (action: DrawingAction, ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement) => {const { tool, points, color, width, text } = action;// 将百分比坐标转换为合成画布的实际像素坐标const actualPoints = points.map(p => ({x: p.x * canvas.width,y: p.y * canvas.height}));// 设置绘图样式ctx.strokeStyle = color;ctx.fillStyle = color;ctx.lineWidth = width;// 根据工具类型绘制不同内容(以马赛克为例)switch (tool) {case 'mosaic':if (actualPoints.length) {drawMosaicToCanvas(actualPoints[0], width, ctx, canvas);}break;// 其他工具(pen/line/rectangle等)的绘制逻辑类似,调用对应方法// ...}
};// 马赛克在截图中的绘制方法
const drawMosaicToCanvas = (position: Point,size: number,ctx: CanvasRenderingContext2D,canvas: HTMLCanvasElement
) => {// 创建临时画布绘制马赛克图案const tempCanvas = document.createElement('canvas');tempCanvas.width = size;tempCanvas.height = size;const tempCtx = tempCanvas.getContext('2d');if (!tempCtx) return;// 绘制马赛克块(固定颜色#50B19D)tempCtx.fillStyle = '#50B19D';tempCtx.strokeStyle = 'rgba(255, 255, 255, 0.2)'; // 块间边框const cellSize = 10; // 马赛克块大小for (let y = 0; y < size; y += cellSize) {for (let x = 0; x < size; x += cellSize) {tempCtx.fillRect(x, y, cellSize, cellSize); // 填充块tempCtx.strokeRect(x, y, cellSize, cellSize); // 块边框}}// 绘制马赛克外边框和角落标记(增强可视性)tempCtx.strokeStyle = 'rgba(80, 177, 157, 0.8)';tempCtx.lineWidth = 2;tempCtx.strokeRect(0, 0, size, size); // 外边框const cornerSize = 8;tempCtx.fillRect(0, 0, cornerSize, cornerSize); // 左上角标记tempCtx.fillRect(size - cornerSize, 0, cornerSize, cornerSize); // 右上角标记tempCtx.fillRect(0, size - cornerSize, cornerSize, cornerSize); // 左下角标记tempCtx.fillRect(size - cornerSize, size - cornerSize, cornerSize, cornerSize); // 右下角标记// 将马赛克绘制到合成画布ctx.drawImage(tempCanvas, position.x - size / 2, position.y - size / 2, size, size);
};
步骤 7:导出并下载截图
将合成画布内容转换为图片 URL,通过临时链接触发下载,并清理资源。
总结
截图功能通过创建合成画布→分层绘制(背景→视频→标注)→导出下载的流程,实现了标注内容与原始画面的精准融合,同时保留了马赛克等隐私保护元素。核心是通过坐标比例转换确保各元素位置对齐,以及临时画布技术处理复杂标注(如马赛克)的绘制。