百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

DeepSeek生成绘制SVG的H5页面(h5 svg)

ztj100 2025-03-19 04:27 9 浏览 0 评论

通过对话,deepseek能辅助完成一个绘图H5页面。如下:


完成之后的效果如下:


如上

咨询步骤


  1. 给选中的图形加轮廓线

选中多线段后出现虚线轮廓

移动时,轮廓线没有移动

  1. 要求轮廓然随拖动移动



  1. 增加右键菜单,用来删除选中对象


添加右键菜单


完整代码




  
  
  SVG Drawing Toolbox
  


  

SVG Drawing Toolbox

<script> const svgContainer = document.getElementById("svg-container"); const codeViewer = document.getElementById("code-viewer"); const contextMenu = document.getElementById("context-menu"); let currentTool = null; let startX, startY; let currentElement = null; // 当前正在绘制的元素 let selectedElements = []; // 存储选中的图形 let isDragging = false; let dragStartX, dragStartY; // 在全局变量中添加虚线预览元素 let previewLine = null; // 线段绘制相关状态 let isDrawingLine = false; // 是否正在绘制线段 let initialPoint = null; // 初始点 let lastPoint = null; // 上一个起始点 let currentLineGroup = null; // 当前线段的组 // Undo/Redo 相关状态 let historyStack = []; // 操作历史栈 let redoStack = []; // 重做栈 // 工具箱事件监听 document.getElementById("select-btn").addEventListener("click", () => { currentTool = "select"; resetLineDrawing(); }); document.getElementById("rect-btn").addEventListener("click", () => { currentTool = "rect"; resetLineDrawing(); }); document.getElementById("circle-btn").addEventListener("click", () => { currentTool = "circle"; resetLineDrawing(); }); document.getElementById("line-btn").addEventListener("click", () => { currentTool = "line"; resetLineDrawing(); }); document.getElementById("text-btn").addEventListener("click", () => { currentTool = "text"; resetLineDrawing(); }); document.getElementById("undo-btn").addEventListener("click", () => { undo(); }); document.getElementById("redo-btn").addEventListener("click", () => { redo(); }); // 自定义上下文菜单事件监听 document.getElementById("end-line").addEventListener("click", () => { resetLineDrawing(); hideContextMenu(); }); document.getElementById("close-loop").addEventListener("click", () => { if (isDrawingLine && initialPoint && lastPoint) { drawLineSegment(lastPoint.x, lastPoint.y, initialPoint.x, initialPoint.y); resetLineDrawing(); } hideContextMenu(); }); document.getElementById("select-drag").addEventListener("click", () => { currentTool = "select"; hideContextMenu(); }); document.getElementById("delete-element").addEventListener("click", () => { // 删除选中的元素 selectedElements.forEach(element => { element.remove(); removeSelectionBorder(element); }); selectedElements = []; saveState(); // 保存状态 updateCodeViewer(); hideContextMenu(); }); function releasePreview(){ if(previewLine){ previewLine.remove(); previewLine = null; } } // 显示自定义上下文菜单 function showContextMenu(x, y) { contextMenu.style.display = "block"; contextMenu.style.left = `${x}px`; contextMenu.style.top = `${y}px`; // 显示或隐藏删除按钮 const deleteButton = document.getElementById("delete-element"); if (selectedElements.length > 0) { deleteButton.style.display = "block"; } else { deleteButton.style.display = "none"; } } // 隐藏自定义上下文菜单 function hideContextMenu() { contextMenu.style.display = "none"; } // 鼠标事件监听 svgContainer.addEventListener("mousedown", (event) => { const x = event.offsetX; const y = event.offsetY; if (event.button === 2) { // 右键点击,显示自定义上下文菜单 event.preventDefault(); showContextMenu(event.clientX, event.clientY); return; } if (currentTool === "select") { // 选择工具:选中图形 console.log(event.target); if (event.target.tagName === "rect" || event.target.tagName === "circle" || event.target.tagName === "text" || event.target.tagName === "line" || event.target.tagName === "g") { var element = event.target; if ((event.target.tagName === "line") && (event.target.parentNode && event.target.parentNode.tagName === "g")) { element = event.target.parentNode; } console.log(element); // 按住 Shift 键多选 if (!event.shiftKey) { selectedElements.forEach(el => { el.classList.remove("selected"); removeSelectionBorder(el); // 移除选中边框 }); selectedElements = []; } if (!selectedElements.includes(element)) { selectedElements.push(element); element.classList.add("selected"); addSelectionBorder(element); // 添加选中边框 } // 开始拖动 isDragging = true; dragStartX = event.offsetX; dragStartY = event.offsetY; } else { // 点击空白区域取消选中 selectedElements.forEach(el => { el.classList.remove("selected"); removeSelectionBorder(el); // 移除选中边框 }); selectedElements = []; } } else if (currentTool === "text") { // 文字工具 const text = prompt("Enter text:"); if (text) { const textElement = document.createElementNS("http://www.w3.org/2000/svg", "text"); textElement.setAttribute("x", x); textElement.setAttribute("y", y); textElement.setAttribute("fill", document.getElementById("stroke-color").value); textElement.setAttribute("font-size", "20"); textElement.textContent = text; svgContainer.appendChild(textElement); saveState(); // 保存状态 updateCodeViewer(); } } else if (currentTool === "line") { if (event.detail === 2) { // 双击清空初始点 resetLineDrawing(); } else if (event.button === 0) { // 左键点击 if (!isDrawingLine) { // 第一次点击,设置初始点和起始点 initialPoint = { x, y }; lastPoint = { x, y }; isDrawingLine = true; // 创建新的线段组 currentLineGroup = document.createElementNS("http://www.w3.org/2000/svg", "g"); svgContainer.appendChild(currentLineGroup); } else { // 后续点击,绘制线段并更新起始点 drawLineSegment(lastPoint.x, lastPoint.y, x, y); lastPoint = { x, y }; // 更新起始点 } } } else if (currentTool === "rect" || currentTool === "circle") { // 矩形或圆形工具 startX = x; startY = y; switch (currentTool) { case "rect": currentElement = document.createElementNS("http://www.w3.org/2000/svg", "rect"); currentElement.setAttribute("x", startX); currentElement.setAttribute("y", startY); break; case "circle": currentElement = document.createElementNS("http://www.w3.org/2000/svg", "circle"); currentElement.setAttribute("cx", startX); currentElement.setAttribute("cy", startY); break; } if (currentElement) { currentElement.setAttribute("stroke", document.getElementById("stroke-color").value); currentElement.setAttribute("stroke-width", document.getElementById("stroke-width").value); currentElement.setAttribute("fill", document.getElementById("fill-color").value); svgContainer.appendChild(currentElement); } } }); svgContainer.addEventListener("mousemove", (event) => { if (currentTool === "select" && isDragging) { // 选择工具:拖动图形 const dx = event.offsetX - dragStartX; const dy = event.offsetY - dragStartY; selectedElements.forEach(element => { if (element.tagName === "rect") { const x = parseFloat(element.getAttribute("x")) + dx; const y = parseFloat(element.getAttribute("y")) + dy; element.setAttribute("x", x); element.setAttribute("y", y); } else if (element.tagName === "circle") { const cx = parseFloat(element.getAttribute("cx")) + dx; const cy = parseFloat(element.getAttribute("cy")) + dy; element.setAttribute("cx", cx); element.setAttribute("cy", cy); } else if (element.tagName === "text") { const x = parseFloat(element.getAttribute("x")) + dx; const y = parseFloat(element.getAttribute("y")) + dy; element.setAttribute("x", x); element.setAttribute("y", y); } else if (element.tagName === "line") { const x1 = parseFloat(element.getAttribute("x1")) + dx; const y1 = parseFloat(element.getAttribute("y1")) + dy; const x2 = parseFloat(element.getAttribute("x2")) + dx; const y2 = parseFloat(element.getAttribute("y2")) + dy; element.setAttribute("x1", x1); element.setAttribute("y1", y1); element.setAttribute("x2", x2); element.setAttribute("y2", y2); } else if (element.tagName === "g") { // 移动整个线段组 Array.from(element.children).forEach(line => { const x1 = parseFloat(line.getAttribute("x1")) + dx; const y1 = parseFloat(line.getAttribute("y1")) + dy; const x2 = parseFloat(line.getAttribute("x2")) + dx; const y2 = parseFloat(line.getAttribute("y2")) + dy; line.setAttribute("x1", x1); line.setAttribute("y1", y1); line.setAttribute("x2", x2); line.setAttribute("y2", y2); }); } updateSelectionBorder(element); // 更新选中边框位置 }); dragStartX = event.offsetX; dragStartY = event.offsetY; updateCodeViewer(); } else if ((currentTool === "rect" || currentTool === "circle") && currentElement) { // 矩形或圆形工具:调整图形大小 const currentX = event.offsetX; const currentY = event.offsetY; switch (currentTool) { case "rect": currentElement.setAttribute("width", Math.abs(currentX - startX)); currentElement.setAttribute("height", Math.abs(currentY - startY)); break; case "circle": const radius = Math.sqrt(Math.pow(currentX - startX, 2) + Math.pow(currentY - startY, 2)); currentElement.setAttribute("r", radius); break; } updateCodeViewer(); }else if (currentTool === "line" && isDrawingLine && lastPoint) { // 线段工具:绘制虚线预览 if (!previewLine) { previewLine = document.createElementNS("http://www.w3.org/2000/svg", "line"); previewLine.setAttribute("stroke", "#000000"); // 虚线颜色 previewLine.setAttribute("stroke-width", document.getElementById("stroke-width").value); previewLine.setAttribute("stroke-dasharray", "5,5"); // 虚线样式 previewLine.setAttribute("fill", "none"); svgContainer.appendChild(previewLine); } const x = event.offsetX; const y = event.offsetY; console.log(lastPoint,x,y) previewLine.setAttribute("x1", lastPoint.x); previewLine.setAttribute("y1", lastPoint.y); previewLine.setAttribute("x2", x); previewLine.setAttribute("y2", y); } }); svgContainer.addEventListener("mouseup", () => { if (currentTool === "select") { isDragging = false; } else if (currentTool === "rect" || currentTool === "circle") { currentElement = null; saveState(); // 保存状态 }else if (currentTool === "line") { // 移除虚线预览 releasePreview(); } }); // 绘制线段 function drawLineSegment(x1, y1, x2, y2) { if (!currentLineGroup) { // 如果 currentLineGroup 未初始化,则创建一个新的组 currentLineGroup = document.createElementNS("http://www.w3.org/2000/svg", "g"); svgContainer.appendChild(currentLineGroup); } const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); line.setAttribute("x1", x1); line.setAttribute("y1", y1); line.setAttribute("x2", x2); line.setAttribute("y2", y2); line.setAttribute("stroke", document.getElementById("stroke-color").value); line.setAttribute("stroke-width", document.getElementById("stroke-width").value); currentLineGroup.appendChild(line); saveState(); // 保存状态 updateCodeViewer(); } // 重置线段绘制状态 function resetLineDrawing() { isDrawingLine = false; initialPoint = null; lastPoint = null; currentLineGroup = null; // 移除虚线预览 releasePreview(); } // 更新代码查看器 function updateCodeViewer() { const serializer = new XMLSerializer(); const svgCode = serializer.serializeToString(svgContainer); codeViewer.value = svgCode; } // 保存当前状态到历史栈 function saveState() { const serializer = new XMLSerializer(); const svgCode = serializer.serializeToString(svgContainer); historyStack.push(svgCode); redoStack = []; // 清空重做栈 } // 撤销操作 function undo() { if (historyStack.length > 1) { redoStack.push(historyStack.pop()); // 将当前状态移到重做栈 const previousState = historyStack[historyStack.length - 1]; svgContainer.innerHTML = previousState; // 恢复到上一个状态 updateCodeViewer(); } } // 重做操作 function redo() { if (redoStack.length > 0) { const nextState = redoStack.pop(); // 从重做栈中取出下一个状态 historyStack.push(nextState); // 将状态移回历史栈 svgContainer.innerHTML = nextState; // 恢复到下一个状态 updateCodeViewer(); } } // 添加选中边框 function addSelectionBorder(element) { const bbox = element.getBBox(); const padding = 5; // 边框比图形大 2px const border = document.createElementNS("http://www.w3.org/2000/svg", "rect"); border.setAttribute("x", bbox.x - padding); border.setAttribute("y", bbox.y - padding); border.setAttribute("width", bbox.width + 2 * padding); border.setAttribute("height", bbox.height + 2 * padding); border.setAttribute("stroke", "#FF0000"); // 红色虚线 border.setAttribute("stroke-width", "2"); border.setAttribute("stroke-dasharray", "5,5"); border.setAttribute("fill", "none"); border.classList.add("selection-border"); svgContainer.appendChild(border); element.border = border; // 将边框引用存储在元素上 } // 移除选中边框 function removeSelectionBorder(element) { if (element && element.border) { element.border.remove(); delete element.border; } } // 更新选中边框位置 function updateSelectionBorder(element) { if (element && element.border) { const bbox = element.getBBox(); const padding = 2; // 边框比图形大 2px element.border.setAttribute("x", bbox.x - padding); element.border.setAttribute("y", bbox.y - padding); element.border.setAttribute("width", bbox.width + 2 * padding); element.border.setAttribute("height", bbox.height + 2 * padding); } } // 点击页面其他区域隐藏上下文菜单 document.addEventListener("click", () => { hideContextMenu(); }); // 阻止浏览器默认右键菜单 document.addEventListener("contextmenu", (event) => { event.preventDefault(); }); </script>

参考资料

上述页面的完整代码在gitee上,路径如下:

https://gitee.com/wapuboy/learning-programming-with-gauss/blob/master/code/javascript/src/svg.html

相关推荐

使用 Pinia ORM 管理 Vue 中的状态

转载说明:原创不易,未经授权,谢绝任何形式的转载状态管理是构建任何Web应用程序的重要组成部分。虽然Vue提供了管理简单状态的技术,但随着应用程序复杂性的增加,处理状态可能变得更具挑战性。这就是为什么...

Vue3开发企业级音乐Web App 明星讲师带你学习大厂高质量代码

Vue3开发企业级音乐WebApp明星讲师带你学习大厂高质量代码下栽课》jzit.top/392/...

一篇文章说清 webpack、vite、vue-cli、create-vue 的区别

webpack、vite、vue-cli、create-vue这些都是什么?看着有点晕,不要怕,我们一起来分辨一下。...

超赞 vue2/3 可视化打印设计VuePluginPrint

今天来给大家推荐一款非常不错的Vue可拖拽打印设计器Hiprint。引入使用//main.js中引入安装import{hiPrintPlugin}from'vue-plugin-...

搭建Trae+Vue3的AI开发环境(vue3 ts开发)

从2024年2025年,不断的有各种AI工具会在自媒体中火起来,号称各种效率王炸,而在AI是否会替代打工人的话题中,程序员又首当其冲。...

如何在现有的Vue项目中嵌入 Blazor项目?

...

Vue中mixin怎么理解?(vue的mixins有什么用)

作者:qdmryt转发链接:https://mp.weixin.qq.com/s/JHF3oIGSTnRegpvE6GSZhg前言...

Vue脚手架安装,初始化项目,打包并用Tomcat和Nginx部署

1.创建Vue脚手架#1.在本地文件目录创建my-first-vue文件夹,安装vue-cli脚手架:npminstall-gvue-cli安装过程如下图所示:创建my-first-vue...

新手如何搭建个人网站(小白如何搭建个人网站)

ElementUl是饿了么前端团队推出的桌面端UI框架,具有是简洁、直观、强悍和低学习成本等优势,非常适合初学者使用。因此,本次项目使用ElementUI框架来完成个人博客的主体开发,欢迎大家讨论...

零基础入门vue开发(vue快速入门与实战开发)

上面一节我们已经成功的安装了nodejs,并且配置了npm的全局环境变量,那么这一节我们就来正式的安装vue-cli,然后在webstorm开发者工具里运行我们的vue项目。这一节有两种创建vue项目...

.net core集成vue(.net core集成vue3)

react、angular、vue你更熟悉哪个?下边这个是vue的。要求需要你的计算机安装有o.netcore2.0以上版本onode、webpack、vue-cli、vue(npm...

使用 Vue 脚手架,为什么要学 webpack?(一)

先问大家一个很简单的问题:vueinitwebpackprjectName与vuecreateprojectName有什么区别呢?它们是Vue-cli2和Vue-cli3创建...

vue 构建和部署(vue项目部署服务器)

普通的搭建方式(安装指令)安装Node.js检查node是否已安装,终端输入node-v会使用命令行(安装)npminstallvue-cli-首先安装vue-clivueinitwe...

Vue.js 环境配置(vue的环境搭建)

说明:node.js和vue.js的关系:Node.js是一个基于ChromeV8引擎的JavaScript运行时环境;类比:Java的jvm(虚拟机)...

vue项目完整搭建步骤(vuecli项目搭建)

简介为了让一些不太清楚搭建前端项目的小白,更快上手。今天我将一步一步带领你们进行前端项目的搭建。前端开发中需要用到框架,那vue作为三大框架主流之一,在工作中很常用。所以就以vue为例。...

取消回复欢迎 发表评论: