DeepSeek生成绘制SVG的H5页面
myzbx 2025-04-26 19:40 13 浏览
通过对话,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
相关推荐
- 怎么恢复7z文件 7z文件删除了怎么恢复
-
7z是一种压缩格式的文件,它运用LZMA压缩算法,该压缩算法的输出稍后被算数编码进行处理以便后续进一步压缩,压缩比十分高。我们可以将文件压缩成这种格式,便于传输,保存,占空间少。了解更多7z文件知识...
- 郎酒让消费者喝得明明白白 算术题里有答案
-
日前,『郎酒酱香产品企业内控准则』颁布,郎酒首次公开酱香产品生产全过程,公布酱香产品产能、储能及投放计划。随后,郎酒官微向消费者发出「品控算术题」有奖问答。郎酒亮出家底,消费者踊跃留言。8天后,谜底揭...
- 学龄前,比识字、算术更重要的是这三件事
-
“为了给孩子选择一家合适的幼儿园,我曾穿梭于纽约各家幼儿园的开放日,这些幼儿员既包括主流的公立幼儿园,还包括那些遥不可及的私人幼儿园。我的目的就是想了解他们的教育理念是什么,到底厉害在哪里,看看对于我...
- 参加CSP-J信奥赛需要掌握数学知识
-
在C++语法的学习中需要储备的数学知识如下①数据类型:需要知道整数、正整数、负整数、小数、判断对错②算术运算符:加法、减法、乘法、除法、取模运算③关系表达式:大于、大于等于、小于、小...
- 1g米饭能做多少深蹲?今天我们来算一算
-
减重我们都知道3分在练,7分在吃,吃这件事情上,真的是每一口都算数。今天我们来算一笔账,1粒米饭可以做多少事情?本着认真负责的态度,今天在食物秤上称了1g米饭,是16粒。根据能量换算:100g米饭是4...
- web 自动化测试,一定得掌握的 8 个核心知识点
-
使用cypress进行端对端测试,和其他的一些框架有一个显著不同的地方,它使用JavaScript作为编程语言。传统主流的selenium框架是支持多语言的,大多数QA会的pytho...
- 大话C语言:赋值运算符(c语言中赋值运算符是什么)
-
赋值运算符是最基本的运算符之一,用于将右侧的值或表达式的计算结果赋给左侧的变量。它是一个二元运算符,意味着它需要两个操作数:一个是目标变量(左侧),另一个是要赋给该变量的值或表达式(右侧)。赋值运算符...
- Vue进阶(幺幺伍):js 将字符串转换为boolean
-
Boolean();参数为0、null和无参数返回false,有参数返回true。Boolean("");//输出为:falseBoolean(null);//输出为...
- mongodb查询的语法(大于,小于,大于或等于,小于或等于等等)
-
1).大于,小于,大于或等于,小于或等于$gt:大于$lt:小于$gte:大于或等于$lte:小于或等于例子:db.collection.find({"field":{$gt:valu...
- Python学不会来打我(21)python表达式知识点汇总
-
在Python中,表达式是由变量、运算符、函数调用等组合而成的语句,用于产生值或执行特定操作。以下是对Python中常见表达式的详细讲解:1.1算术表达式涉及数学运算的表达式。例如:a=5b...
- C|数据存储地址与字节偏移、数据索引
-
话说C是面向内存的编程语言。数据要能存得进去,取得出来,且要考虑效率。不管是顺序存储还是链式存储,其寻址方式总是很重要。顺序存储是连续存储。同质结构的数组通过其索引表示位置偏移,异质结构的结构体通过其...
- 下班后累懵?4 个 JS 手写题帮你搞定前端面试高频考点
-
打工人下班后最痛苦的事,莫过于拖着疲惫的身子还要啃前端面试题吧?看着那些密密麻麻的JS代码,脑子都快转不动了!别担心,今天咱就用轻松的方式,带你吃透4道高频手写题,让你在面试时自信满满,再也不...
- 嵌入式数据库sqlite3【进阶篇】-子句和函数的使用,小白一文入门
-
sqlite在《嵌入式数据库sqlite3命令操作基础篇-增删改查,小白一文入门》一文中讲解了如何实现sqlite3的基本操作增删改查,本文介绍一些其他复杂一点的操作。比如where、orderby...
- 前缀表达式与后缀表达式(前缀表达式后缀表达式中缀表达式计算)
-
昨天晚上和儿子一起学习了前缀表达式和后缀表达式。这应该是字符串算式如何被计算机识别并计算的2种方法。本来是想先给他讲一个逆波兰式(后缀表达式),以后再讲前缀表达式。没想到他还挺聪明,很快就把2个都掌握...
- Python快速入门教程1:基本语法、数据类型、运算符、数字字符串
-
Python3的基础教程,涵盖了基本语法、数据类型、类型转换、解释器、注释、运算符、数字和字符串等内容,并附有使用实例场景。Python3的基础教程,涵盖了基本语法、数据类型、类型转换、解释器、注释、...
- 一周热门
- 最近发表
- 标签列表
-
- 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 轮廓宽度 (31)
- CSS 谷歌字体 (33)
- CSS 链接 (31)
- CSS 定位 (31)
- CSS 图片库 (32)
- CSS 图像精灵 (31)
- SVG 文本 (32)
- 时钟启动 (33)
- HTML 游戏 (34)