DeepSeek生成绘制SVG的H5页面
myzbx 2025-04-26 19:40 4 浏览
通过对话,deepseek能辅助完成一个绘图H5页面。如下:
完成之后的效果如下:
咨询步骤
- 给选中的图形加轮廓线
- 要求轮廓然随拖动移动
- 增加右键菜单,用来删除选中对象
完整代码
<!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...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 基础教程 (29)
- HTML 简介 (30)
- HTML 响应式设计 (31)
- HTML URL 编码 (32)
- HTML Web 服务器 (31)
- HTML 表单属性 (32)
- HTML 音频 (31)
- HTML5 支持 (33)
- HTML API (36)
- HTML 总结 (32)
- HTML 全局属性 (32)
- HTML 事件 (31)
- HTML 画布 (32)
- HTTP 方法 (30)
- 键盘快捷键 (30)
- CSS 语法 (35)
- CSS 选择器 (30)
- CSS 轮廓 (30)
- CSS 轮廓宽度 (31)
- CSS 谷歌字体 (33)
- CSS 链接 (31)
- CSS 中级教程 (30)
- CSS 定位 (31)
- CSS 图片库 (32)
- CSS 图像精灵 (31)