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

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

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

相关推荐

30天学会Python编程:16. Python常用标准库使用教程

16.1collections模块16.1.1高级数据结构16.1.2示例...

强烈推荐!Python 这个宝藏库 re 正则匹配

Python的re模块(RegularExpression正则表达式)提供各种正则表达式的匹配操作。...

Python爬虫中正则表达式的用法,只讲如何应用,不讲原理

Python爬虫:正则的用法(非原理)。大家好,这节课给大家讲正则的实际用法,不讲原理,通俗易懂的讲如何用正则抓取内容。·导入re库,这里是需要从html这段字符串中提取出中间的那几个文字。实例一个对...

Python数据分析实战-正则提取文本的URL网址和邮箱(源码和效果)

实现功能:Python数据分析实战-利用正则表达式提取文本中的URL网址和邮箱...

python爬虫教程之爬取当当网 Top 500 本五星好评书籍

我们使用requests和re来写一个爬虫作为一个爱看书的你(说的跟真的似的)怎么能发现好书呢?所以我们爬取当当网的前500本好五星评书籍怎么样?ok接下来就是学习python的正确姿...

深入理解re模块:Python中的正则表达式神器解析

在Python中,"re"是一个强大的模块,用于处理正则表达式(regularexpressions)。正则表达式是一种强大的文本模式匹配工具,用于在字符串中查找、替换或提取特定模式...

如何使用正则表达式和 Python 匹配不以模式开头的字符串

需要在Python中使用正则表达式来匹配不以给定模式开头的字符串吗?如果是这样,你可以使用下面的语法来查找所有的字符串,除了那些不以https开始的字符串。r"^(?!https).*&...

先Mark后用!8分钟读懂 Python 性能优化

从本文总结了Python开发时,遇到的性能优化问题的定位和解决。概述:性能优化的原则——优化需要优化的部分。性能优化的一般步骤:首先,让你的程序跑起来结果一切正常。然后,运行这个结果正常的代码,看看它...

Python“三步”即可爬取,毋庸置疑

声明:本实例仅供学习,切忌遵守robots协议,请不要使用多线程等方式频繁访问网站。#第一步导入模块importreimportrequests#第二步获取你想爬取的网页地址,发送请求,获取网页内...

简单学Python——re库(正则表达式)2(split、findall、和sub)

1、split():分割字符串,返回列表语法:re.split('分隔符','目标字符串')例如:importrere.split(',','...

Lavazza拉瓦萨再度牵手上海大师赛

阅读此文前,麻烦您点击一下“关注”,方便您进行讨论和分享。Lavazza拉瓦萨再度牵手上海大师赛标题:2024上海大师赛:网球与咖啡的浪漫邂逅在2024年的上海劳力士大师赛上,拉瓦萨咖啡再次成为官...

ArkUI-X构建Android平台AAR及使用

本教程主要讲述如何利用ArkUI-XSDK完成AndroidAAR开发,实现基于ArkTS的声明式开发范式在android平台显示。包括:1.跨平台Library工程开发介绍...

Deepseek写歌详细教程(怎样用deepseek写歌功能)

以下为结合DeepSeek及相关工具实现AI写歌的详细教程,涵盖作词、作曲、演唱全流程:一、核心流程三步法1.AI生成歌词-打开DeepSeek(网页/APP/API),使用结构化提示词生成歌词:...

“AI说唱解说影视”走红,“零基础入行”靠谱吗?本报记者实测

“手里翻找冻鱼,精心的布局;老漠却不言语,脸上带笑意……”《狂飙》剧情被写成歌词,再配上“科目三”背景音乐的演唱,这段1分钟30秒的视频受到了无数网友的点赞。最近一段时间随着AI技术的发展,说唱解说影...

AI音乐制作神器揭秘!3款工具让你秒变高手

在音乐创作的领域里,每个人都有一颗想要成为大师的心。但是面对复杂的乐理知识和繁复的制作过程,许多人的热情被一点点消磨。...

取消回复欢迎 发表评论: