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

DeepSeek生成绘制SVG的H5页面

myzbx 2025-04-26 19:40 4 浏览

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


完成之后的效果如下:


咨询步骤


  1. 给选中的图形加轮廓线
  1. 要求轮廓然随拖动移动



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



完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SVG Drawing Toolbox</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
    }
    #toolbox {
      margin-bottom: 10px;
    }
    #toolbox button, #toolbox input {
      margin: 5px;
    }
    #svg-container {
      width: 800px;
      height: 600px;
      border: 1px solid #ccc;
      position: relative;
    }
    #code-viewer {
      width: 800px;
      height: 100px;
      margin-top: 10px;
      font-family: monospace;
    }
    /* 自定义上下文菜单样式 */
    #context-menu {
      position: absolute;
      display: none;
      background-color: white;
      border: 1px solid #ccc;
      box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
      z-index: 1000;
    }
    #context-menu button {
      display: block;
      width: 100%;
      padding: 8px 16px;
      text-align: left;
      border: none;
      background: none;
      cursor: pointer;
    }
    #context-menu button:hover {
      background-color: #f0f0f0;
    }
  </style>
</head>
<body>
  <h1>SVG Drawing Toolbox</h1>
  <div id="toolbox">
    <button id="select-btn">Select</button>
    <button id="rect-btn">Rectangle</button>
    <button id="circle-btn">Circle</button>
    <button id="line-btn">Line</button>
    <button id="text-btn">Text</button>
    <button id="undo-btn">Undo</button>
    <button id="redo-btn">Redo</button>
    <input type="color" id="stroke-color" value="#000000">
    <input type="color" id="fill-color" value="#ffffff">
    <input type="number" id="stroke-width" min="1" max="10" value="2">
  </div>
  <svg id="svg-container" xmlns="http://www.w3.org/2000/svg"></svg>
  <textarea id="code-viewer" readonly></textarea>

  <!-- 自定义上下文菜单 -->
  <div id="context-menu">
    <button id="end-line">结束线段(双击)</button>
    <button id="close-loop">闭环线段(右键)</button>
    <button id="select-drag">选中拖动</button>
    <button id="delete-element" style="display: none;">删除</button>
  </div>

  <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>
</body>
</html>

参考资料

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

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

相关推荐

建模大神都在用的软件,哝晓得不?

各位宝宝们,阿瑶又来啦!一日不见,甚是想念。今天阿瑶这篇文章是个连载文章连载四天大家走过路过不要错过哦!今天阿瑶先给大家来讲一讲Maya建模类型中的第一个多边形建模。各位宝宝们,赶紧拿好你们的小板凳,...

神器推荐 | 制作多边形网格从此不用愁

近来许多大数据分析、网络社群方面的PPT,特别喜欢使用多边形网格作为修饰,借以表示连接、关系、节点等抽象概念,比如▼虽然使用PPT自带的图形工具也能做出来,但是纯手工绘制那是相当耗时耗力,谁用谁知道▼...

谷歌最强推理模型gemini2.5pro登场,强的离谱~

25号晚上,谷歌直接放了一个大招,直接推出了gemini2.5pro的下一代推理模型。强到什么程度,据官网介绍的,在一系列需要高级推理的基准测试中都处于领先地位,并且是甩掉了后面模型一大截的差距。就好...

设计大神不传之秘,这个PPT隐藏的排版神器,让你提前1小时下班

最近好多学员在群里给我吐槽,说做工作汇报的时候经常碰到一大堆文字的时间轴页面,不管怎么排版都感觉又乱又丑,一眼看不到重点,就像这样:如果随便找一张模板套上去,很有可能显得浮夸:不如找来一张折线图,上升...

网站分享 | 想要作品有创意?不如学学如何玩转半色调吧

正当你想要为自己的设计作品增添新的创意元素发愁时,不如试试「半色调」能否给你带来新的灵感!什么是半色调?半色调又称灰度级,它是反映图像亮度层次、黑白对比变化的技术指标。也就是通过运用点、条纹或其他形...

想做半色调+低多边形+条纹效果?这个网站帮你一键生成!

如果想要为设计作品增添一些新意的元素,或许今天这个「矢量半色调」网站能带给你一些启发!什么是「半色调」效果呢,「半色调」是指运用点、条纹或其他形状的图案,根据元素大小或者疏密的不同,从而构成从远处看起...

Excalidraw-免费的白板应用,能够画各种流程图架构图

一款完全免费的手绘风格绘图在线应用,能快速画出漂亮的流程图、示意图甚至是图表。关于ExcalidrawExcalidraw是一款轻量的手绘风格电子白板在线应用,无论是Windows/macO...

时间轴动画怎么加才惊艳?简单,腾讯御用PPT设计师直接爆料

大家应该收集或者见过很多不错的时间轴的页面设计吧?时间轴算是PPT制作中经常遇到的页面类型,个人述职、项目路演、企业介绍等等,都会用到时间轴来让用户更加直观地了解个人或公司。比如腾讯的时间轴页:又比如...

图片无限放大也不会模糊的秘密,矢量图的那些事

当你想在你的文档或者幻灯片中插入一个图片,感觉图片太小排版不太美观,我们放大一下图片,发现图片因为放大而变得模糊,严重影响视觉感受。这个视频我会告诉如何让图片放大、再放大都不会失真的方法。首先我们先来...

用热搜的方式打开推文?

热搜,向来是全民瞩目的焦点。要是把热搜元素融入到推文之中,那推文的趣味性必定直线上升!想必大家都很好奇,怎样在SVG交互推文中打造出热搜效果呢?不少品牌方账号中,90%以上的内容都采用了SVG,交...

羊角螺线如何应用到平面设计中?

羊角螺线是一段数学函数曲线。多用作缓和曲线,缓和直路线与圆曲路线之间曲线变化的作用。羊角螺线的特点是,从坐标原点开始,起始曲率为0,函数曲线的曲率呈线性增长。也就是说,羊角螺线可以完美链接直线与任何圆...

cad转svg的软件有哪些?为你介绍这四个软件

cad转svg的软件有哪些?CAD(计算机辅助设计)是一种广泛使用的工具,用于创建和修改设计图纸。SVG(可缩放矢量图形)是一种开放标准,用于在Web上显示矢量图形。CAD转SVG是将CAD图纸转换为...

图片格式那么多,哪种更适合你?

本文介绍和比较几种常见图片文件格式的优缺点,并介绍不同的文件格式对Web应用程序性能的影响。有损vs无损图片文件格式有可能会对图片的文件大小进行不同程度的压缩,图片的压缩分为有损压缩和无损压缩两种。有...

一篇文章教会你使用SVG 画多边形

作者:前端进阶者来源:前端进阶学习交流大家好,我是前端进阶者。polygon元素定义了一个由一组首尾相连的直线线段构成的闭合多边形形状,最后一点连接到第一点。元素通常用于绘制具有多个(3个或更多)...

SVG 绘制曲折线

本节我们来学习如何在SVG中绘制曲折线,绘制曲折线可以使用<polyline>元素来实现。如何绘制曲折线曲折线就是通过一系列的直线连接多个点,然后组合成任意形状。曲折线可以通过S...