功能 安卓app上面nvue 视频上方绘制(vue2)
①新建一个draw.html文件(里面功能有画笔 清空 橡皮擦 改颜色 禁用画笔 截图-是视频画面加绘制合成一张图片截图)
②
webViewUrl: '/static/draw.html',③
<!-- 画布 绘制层 --> <web-view v-if="true" ref="drawWebview" :src="webViewUrl" class="draw-layer" ></web-view>④.nvue页面方法html我吧方法挂在Windows上面的直接绑定ref使用
// 切换颜色 setColor(color) { this.huahuaAction=''; this.pen.color = color const wv = this.$refs.drawWebview; if (wv) wv.evalJS(`setColorFunc('${color}')`); }, // 切换橡皮擦 useEraser(val) { this.huahuaAction=val; const wv = this.$refs.drawWebview; if (wv && wv.evalJS) wv.evalJS('setEraserFunc()'); }, // 切换画笔 usePen() { const wv = this.$refs.drawWebview; if (wv && wv.evalJS) wv.evalJS('setPenFunc()'); }, // 清空画布 clearCanvas(val) { this.huahuaAction=val; const wv = this.$refs.drawWebview; if (wv && wv.evalJS) wv.evalJS('clearCanvasFunc()'); }, // 撤销 undo() { this.huahuaAction=''; // 撤销 const wv = this.$refs.drawWebview; if (wv && wv.evalJS) wv.evalJS('undoFunc()'); }, // 重做 redo() { this.huahuaAction=''; const wv = this.$refs.drawWebview; if (wv && wv.evalJS) wv.evalJS('redoFunc()'); }, // 画笔 handleHuabi() { this.huahuaAction=''; this.showDraw = true this.usePen() this.$refs.drawWebview.evalJS('enableDrawFunc()') //this.showDraw = !this.showDraw },⑥关键的draw.html原生绘制代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>视频截图 + 涂鸦合成</title> <style> html, body { margin: 0; padding: 0; height: 100%; background: transparent; overflow: hidden; } #canvas { display: block; touch-action: none; background-color: transparent; } </style> </head> <body> <canvas id="canvas"></canvas> <script> /* ================= 基础初始化 ================= */ const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); let drawEnabled = true; function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } window.addEventListener("resize", resizeCanvas); resizeCanvas(); ctx.lineCap = "round"; ctx.lineJoin = "round"; /* ================= 状态变量 ================= */ let drawing = false; let lastX = 0; let lastY = 0; let strokeColor = "#ff0000"; let penWidth = 2; let strokeWidth = penWidth; let mode = "draw"; // draw | erase let currentStroke = null; const history = []; const redoStack = []; /* ================= 绘制核心 ================= */ function drawLine(x1, y1, x2, y2, stroke) { ctx.save(); if (stroke.mode === "erase") { ctx.globalCompositeOperation = "destination-out"; ctx.lineWidth = stroke.width * 2; } else { ctx.globalCompositeOperation = "source-over"; ctx.strokeStyle = stroke.color; ctx.lineWidth = stroke.width; } ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); ctx.restore(); } function redrawAll() { ctx.clearRect(0, 0, canvas.width, canvas.height); history.forEach((stroke) => { for (let i = 1; i < stroke.points.length; i++) { const p1 = stroke.points[i - 1]; const p2 = stroke.points[i]; drawLine(p1.x, p1.y, p2.x, p2.y, stroke); } }); } /* ================= 手势事件 ================= */ canvas.addEventListener("touchstart", (e) => { if (!drawEnabled) return; const t = e.touches[0]; drawing = true; lastX = t.clientX; lastY = t.clientY; currentStroke = { mode, color: strokeColor, width: strokeWidth, points: [{ x: lastX, y: lastY }], }; }); canvas.addEventListener("touchmove", (e) => { if (!drawing) return; const t = e.touches[0]; const x = t.clientX; const y = t.clientY; drawLine(lastX, lastY, x, y, currentStroke); currentStroke.points.push({ x, y }); lastX = x; lastY = y; }); canvas.addEventListener("touchend", () => { if (!drawing) return; drawing = false; if (currentStroke && currentStroke.points.length > 1) { history.push(currentStroke); redoStack.length = 0; } currentStroke = null; }); /* ================= 视频截图 + 涂鸦合成 ================= */ window.mergeWithVideo = function (videoSrc) { alert("收到路径:" + videoSrc); console.log("HTML: 收到视频截图路径", videoSrc); if (!videoSrc.startsWith("file://")) { videoSrc = "file://" + videoSrc; } const img = new Image(); img.onload = function () { try { ctx.clearRect(0, 0, canvas.width, canvas.height); // 1. 绘制视频截图 ctx.drawImage(img, 0, 0, canvas.width, canvas.height); // 2. 重绘历史涂鸦 history.forEach((stroke) => { for (let i = 1; i < stroke.points.length; i++) { const p1 = stroke.points[i - 1]; const p2 = stroke.points[i]; drawLine(p1.x, p1.y, p2.x, p2.y, stroke); } }); // 3. 导出 exportCanvas(); } catch (e) { alert("合成失败:" + e.message); } }; img.onerror = function () { alert("图片加载失败"); }; img.src = videoSrc; }; /* ================= 导出画布 ================= */ // 将 base64 转换为 File/Blob 对象,以便使用 fetch 上传 function base64ToFileObject(base64Data, fileName = "temp_image.png") { // 移除base64前缀 const base64WithoutPrefix = base64Data.replace( /^data:image\/\w+;base64,/, "" ); const fileType = base64Data.split("/")[1].split(";")[0] || "png"; // 将base64解码为二进制数据 const binaryString = atob(base64WithoutPrefix); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } // 创建 Blob 对象 const blob = new Blob([bytes], { type: `image/${fileType}` }); // 如果支持 File 构造函数,创建 File 对象 if (typeof File !== "undefined") { return new File([blob], `${fileName}_${Date.now()}.${fileType}`, { type: `image/${fileType}`, }); } return blob; } // 使用 fetch 上传 File/Blob 对象 async function uploadFileUsingFetch( fileObject, serverUrl = "http://47.666.666.234:8080/photo/upload" ) { const formData = new FormData(); formData.append( "file", fileObject, fileObject.name || "canvas_screenshot.png" ); try { const response = await fetch(serverUrl, { method: "POST", body: formData, }); if (response.ok) { const result = await response.json(); console.log("上传成功:", result); return result; } else { throw new Error(`上传失败,状态码: ${response.status}`); } } catch (error) { console.error("上传过程中发生错误:", error); throw error; } } // 使用方法 把视频截图的图片路径通过nvue 传进来就能把历史绘制上去然后合成一张file传给接口 async function exportCanvas() { try { const base64 = canvas.toDataURL("image/png"); const fileObject = base64ToFileObject(base64, "canvas_screenshot"); await uploadFileUsingFetch(fileObject); } catch (e) { alert("导出失败:" + e.message); } } /* ================= nvue 可调用接口 ================= */ window.clearCanvasFunc = function () { history.length = 0; redoStack.length = 0; redrawAll(); }; window.setColorFunc = function (color) { strokeColor = color; mode = "draw"; strokeWidth = penWidth; }; window.setEraserFunc = function () { mode = "erase"; strokeWidth = penWidth * 10; }; window.setPenFunc = function () { mode = "draw"; strokeWidth = penWidth; }; window.undoFunc = function () { if (!history.length) return; redoStack.push(history.pop()); redrawAll(); }; window.redoFunc = function () { if (!redoStack.length) return; history.push(redoStack.pop()); redrawAll(); }; window.enableDrawFunc = function () { drawEnabled = true; }; window.disableDrawFunc = function () { drawEnabled = false; drawing = false; currentStroke = null; }; window.exportCanvas = exportCanvas; </script> </body> </html>