.nvue页面实现画笔绘制功能,用原生html导入nvue页面使用还可以截图(画笔 清空 橡皮擦 改颜色 禁用画笔 截图-是视频画面加绘制合成一张图片截图)-我花80块钱找淘宝都没弄出来,自己写的

功能 安卓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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1206575.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

搞懂大数据CAP定理,为你的职业发展添砖加瓦

搞懂大数据CAP定理&#xff1a;从原理到实战&#xff0c;为你的分布式架构能力赋能 引言&#xff1a;为什么你的分布式系统总在“纠结”&#xff1f; 假设你正在设计一个电商库存系统&#xff1a; 运营说“不能超卖&#xff01;”——这要求数据绝对一致&#xff08;买一件库…

WebGL Shader性能优化

&#x1f680; WebGL Shader性能优化全指南&#xff08;结合Cesium实战&#xff09; WebGL Shader运行在GPU的SIMD&#xff08;单指令多数据&#xff09;架构上&#xff0c;与CPU的分支预测逻辑完全不同。条件语句&#xff08;if-else、switch&#xff09;会导致GPU线程束&…

手机外壳平面度用什么设备检测快?SIMSCAN精细模式+自动报告方案推荐

手机外壳平面度高效检测方案:思看科技SIMSCAN-E三维扫描仪精细模式深度解析 body { font-family: "Microsoft YaHei", sans-serif; line-height: 1.8; color: rgba(51, 51, 51, 1); max-width: 1200px; mar…

建筑BIM模型怎么从实体建筑生成?三维扫描仪推荐TrackScan-Sharp!

建筑BIM模型逆向生成与思看科技TrackScan-Sharp大范围空间扫描解决方案 body { font-family: "Microsoft YaHei", Arial, sans-serif; line-height: 1.8; color: rgba(51, 51, 51, 1); max-width: 1200px; m…

HBase与Quarkus:Kubernetes原生Java

《HBase + Quarkus 实战:构建Kubernetes原生Java应用的最佳实践》 一、引言:传统Java与云原生的“矛盾”,如何破解? 作为Java开发者,你是否遇到过这样的痛点: 写了一个连接HBase的Java应用,本地运行没问题,但部署到Kubernetes后,启动要等好几秒,内存占用高达500MB+…

详细介绍:《 Linux 点滴漫谈: 四 》文件权限与用户管理

详细介绍:《 Linux 点滴漫谈: 四 》文件权限与用户管理pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas…

阿里拟析平头哥以赴市:论芯片分拆之战略深意

岁在丙午&#xff0c;正月廿二日&#xff0c;有西媒彭博传讯&#xff1a;阿里巴巴集团谋析其芯片子公司“平头哥”&#xff08;T-Head&#xff09;为独立之体&#xff0c;拟推之上市。闻者哗然&#xff0c;美股盘前应声而起&#xff0c;涨幅逾四。此非寻常之举&#xff0c;实乃…

多边形剪裁算法

多边形剪裁算法 用 box 剪裁任意多边形。 算法原理: 原多边形↓ 用 LEFT 裁剪 中间多边形↓ 用 RIGHT 裁剪 中间多边形↓ 用 BOTTOM 裁剪 中间多边形↓ 用 TOP 裁剪 最终结果每一步都保证输出多边形在当前剪裁边的内侧…

铸件毛坯余量如何精准测量分析?自动生成偏差色谱图产品推荐

思看科技ScanViewer:铸件毛坯余量精准测量分析与偏差色谱图生成解决方案 body { font-family: Arial, sans-serif; line-height: 1.6; margin: 0; padding: 20px; background-color: rgba(244, 244, 244, 1); color: …

2026年深圳APP定制开发外包公司权威榜单发布

随着深圳在科技创新领域的持续崛起,APP定制开发市场竞争日益激烈。各家公司依托自身技术优势和行业经验,正在为企业提供更高效、创新的数字化解决方案。本文为您呈现2026年深圳APP定制开发公司权威榜单,带您了解这些…

量具测不准太慢?模具精度检查难题破解!思看3DeVOK MT+Polyworks方案推荐

模具尺寸检测新纪元:告别测不准与低效,思看科技三维扫描解决方案 body { font-family: "Microsoft YaHei", sans-serif; line-height: 1.8; color: rgba(51, 51, 51, 1); max-width: 1200px; margin: 0 au…

提升大数据处理效率,聚焦 ETL 核心策略

提升大数据处理效率&#xff0c;聚焦 ETL 核心策略 关键词&#xff1a;ETL、大数据处理、数据抽取、数据转换、数据加载、效率优化、数据质量 摘要&#xff1a;在大数据时代&#xff0c;企业每天要处理海量数据&#xff0c;但数据从“原始杂乱”到“可用资产”的关键桥梁——ET…

2026必备!继续教育必看!TOP10一键生成论文工具深度测评

2026必备&#xff01;继续教育必看&#xff01;TOP10一键生成论文工具深度测评 2026年继续教育论文工具测评&#xff1a;精准选择&#xff0c;高效写作 在继续教育领域&#xff0c;论文撰写已成为许多学员必须面对的重要任务。随着AI技术的不断进步&#xff0c;各类论文生成工…

大数据领域数据服务在旅游科技领域的应用探索

大数据领域数据服务在旅游科技领域的应用探索关键词&#xff1a;大数据数据服务、旅游科技、个性化推荐、动态定价、客流管理、智能行程规划、数据驱动决策摘要&#xff1a;本文以“大数据如何赋能旅游行业智能化升级”为核心&#xff0c;结合旅游场景中的实际需求&#xff0c;…

URC 分流是什么意思 + 为什么必须做 + ESP-IDF 可直接用的代码框架

下面是一篇我帮你把 “URC 分流是什么意思 为什么必须做 ESP-IDF 可直接用的代码框架” 合并成一整篇的文章&#xff0c;你直接复制即可 ✅ ESP32 4G 模组&#xff1a;URC 分流是什么意思&#xff1f;为什么必须做&#xff1f;&#xff08;附完整代码&#xff09; 在 ESP32…

ESP_ERR_OTA_VALIDATE_FAILED 的意思非常明确

这个错误 ESP_ERR_OTA_VALIDATE_FAILED 的意思非常明确&#xff1a;你写进 OTA 分区的固件镜像在 esp_ota_end() 做校验时&#xff0c;被判定为“不是一个有效的 ESP 镜像”或“镜像已损坏/不完整”。 所以 OTA 结束失败&#xff0c;新的固件不会被设置为可启动。你截图里这行也…

结论是:不是单一问题,你这边至少有 2 类崩溃,而且都和 ML307 的 AT/UART收发链路 + 异常数据处理 强相关

我把你这几份 C3 ML307 的崩溃日志按“最像根因”的方向拆开看了&#xff0c;结论是&#xff1a;不是单一问题&#xff0c;你这边至少有 2 类崩溃&#xff0c;而且都和 ML307 的 AT/UART收发链路 异常数据处理 强相关。 ① 开机就崩&#xff1a;Load access fault&#xff08…

Golang 与 Kubernetes:实现自动化备份与恢复

Golang 与 Kubernetes:实现自动化备份与恢复 关键词:Golang、Kubernetes、自动化备份、自定义资源(CRD)、云原生、状态管理、快照恢复 摘要:在云原生时代,Kubernetes(K8s)已成为容器编排的事实标准,但如何保障集群中关键应用(如数据库、配置中心)的状态安全仍是核心…

Lua基础语法(下)

九、函数 9.1 初识函数 函数是指一段在一起的、可以做某一件事儿的程序,也叫做子程序。 在前面的内容中,我们已经接触过了函数的调用,这个函数就是前面用到了很多次的print(...)。 调用函数只需要按下面的格式即可:…

结课设计.

1.配置本地光盘镜像为yum源2.测试网络连通性3.安装php4.安装nginx5.安装数据库6.数据库配置创建luntan数据库修改密码7.设置论坛安装文件并赋予权限修改第42行的路径8.查询ip9.安装论坛软件