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

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

ztj100 2025-03-19 04:27 16 浏览 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

相关推荐

其实TensorFlow真的很水无非就这30篇熬夜练

好的!以下是TensorFlow需要掌握的核心内容,用列表形式呈现,简洁清晰(含表情符号,<300字):1.基础概念与环境TensorFlow架构(计算图、会话->EagerE...

交叉验证和超参数调整:如何优化你的机器学习模型

准确预测Fitbit的睡眠得分在本文的前两部分中,我获取了Fitbit的睡眠数据并对其进行预处理,将这些数据分为训练集、验证集和测试集,除此之外,我还训练了三种不同的机器学习模型并比较了它们的性能。在...

机器学习交叉验证全指南:原理、类型与实战技巧

机器学习模型常常需要大量数据,但它们如何与实时新数据协同工作也同样关键。交叉验证是一种通过将数据集分成若干部分、在部分数据上训练模型、在其余数据上测试模型的方法,用来检验模型的表现。这有助于发现过拟合...

深度学习中的类别激活热图可视化

作者:ValentinaAlto编译:ronghuaiyang导读使用Keras实现图像分类中的激活热图的可视化,帮助更有针对性...

超强,必会的机器学习评估指标

大侠幸会,在下全网同名[算法金]0基础转AI上岸,多个算法赛Top[日更万日,让更多人享受智能乐趣]构建机器学习模型的关键步骤是检查其性能,这是通过使用验证指标来完成的。选择正确的验证指...

机器学习入门教程-第六课:监督学习与非监督学习

1.回顾与引入上节课我们谈到了机器学习的一些实战技巧,比如如何处理数据、选择模型以及调整参数。今天,我们将更深入地探讨机器学习的两大类:监督学习和非监督学习。2.监督学习监督学习就像是有老师的教学...

Python教程(三十八):机器学习基础

...

Python 模型部署不用愁!容器化实战,5 分钟搞定环境配置

你是不是也遇到过这种糟心事:花了好几天训练出的Python模型,在自己电脑上跑得顺顺当当,一放到服务器就各种报错。要么是Python版本不对,要么是依赖库冲突,折腾半天还是用不了。别再喊“我...

超全面讲透一个算法模型,高斯核!!

...

神经网络与传统统计方法的简单对比

传统的统计方法如...

AI 基础知识从0.1到0.2——用“房价预测”入门机器学习全流程

...

自回归滞后模型进行多变量时间序列预测

下图显示了关于不同类型葡萄酒销量的月度多元时间序列。每种葡萄酒类型都是时间序列中的一个变量。假设要预测其中一个变量。比如,sparklingwine。如何建立一个模型来进行预测呢?一种常见的方...

苹果AI策略:慢哲学——科技行业的“长期主义”试金石

苹果AI策略的深度原创分析,结合技术伦理、商业逻辑与行业博弈,揭示其“慢哲学”背后的战略智慧:一、反常之举:AI狂潮中的“逆行者”当科技巨头深陷AI军备竞赛,苹果的克制显得格格不入:功能延期:App...

时间序列预测全攻略,6大模型代码实操

如果你对数据分析感兴趣,希望学习更多的方法论,希望听听经验分享,欢迎移步宝藏公众号...

AI 基础知识从 0.4 到 0.5—— 计算机视觉之光 CNN

...

取消回复欢迎 发表评论: