DeepSeek生成绘制SVG的H5页面
myzbx 2025-04-26 19:40 23 浏览
通过对话,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
相关推荐
- 泰国野猪足球队一17岁队员在英去世,曾被困洞穴18天后奇迹获救
-
泰国网图当地时间2月14日,现年17岁的泰国野猪队队员多姆(Dom,本名DuangpetchPromthep)在英国去世,他曾于2018年被困于洞穴18天后奇迹获救。据英国广播公司(BBC)报道,...
- 你需要知道的 19 个 console 实用调试技巧
-
大家好,我是Echa。之前给大家介绍了《H5移动端调试攻略——超实用》,有兴趣的小伙们可以回过头看看。浏览器的开发者工具为我们提供了强大的调试系统,可以用来查看DOM树结构、CSS样式调试、动画调试...
- 深圳嘉华学校:什么是JQuery?_深圳嘉华职业技术学校
-
什么是JQuery?这里将由北大青鸟深圳嘉华来介绍下关于JQuery部分知识,希望能让大家对JQuery有初步的映象。JQuery是继prototype之后又一个优秀的Javascript库。它是轻量...
- Vue3 实现一个简单的放大动画_vue放大图片
-
设计思路定位动画我们在之前已经实现了。那么这里只要考虑如何实现放大动画,最后将两者结合起来就好。从后端拿到的返回值是一个固定长度的数组,所以这里还是用div利用flex布局将图片平铺展示,利用...
- JavaScript 事件循环机制详解_js事件循环队列
-
记录、分享IT相关知识和见闻!想要了解更多软件相关知识的朋友!记得右上角添加【关注】,支持一下!JavaScript是单线程语言,意味着同一时间只能执行一个任务。为了处理异步操作(如定时器、网络请求...
- 前端性能优化新维度:渲染流水线深度解析
-
当开发者沉迷于框架选型和语法特性时,浏览器渲染引擎正在以每秒60帧的速度执行着精密计算。本文将揭示现代浏览器的渲染流水线工作原理,探索超越传统性能优化的新思路。一、渲染流水线的五大阶段1.JavaSc...
- 如果看未来,DOM应该也不是答案_如果知道未来
-
Managershare:未来,还会有连通APP的APP。不过,一切都不会基于网页。有一个词"手机网站"(mobileweb),指供手机浏览的网站,但它是不存在的。人们提到"移动互联网"的时候,其实...
- Springboot之登录模块探索(含Token,验证码,网络安全等知识)
-
简介登录模块很简单,前端发送账号密码的表单,后端接收验证后即可~淦!可是我想多了,于是有了以下几个问题(里面还包含网络安全问题):1.登录时的验证码2.自动登录的实现3.怎么维护前后端登录状态在这和大...
- 总结100+前端优质库,让你成为前端百事通
-
1年多时间,陆陆续续整理了一些常用且实用的开源项目,方便大家更高效地学习和工作.js相关库js常用工具类「lodash」一个一致性、模块化、高性能的JavaScript实用工具库。「xij...
- 基于ssm的XATU实验室安全管理系统 [SSM]-计算机毕业设计源码+文档
-
摘要:实验室安全管理是高校和科研机构工作中的重要环节。本文介绍了基于SSM(Spring+SpringMVC+MyBatis)框架的XATU实验室安全管理系统。该系统涵盖系统用户管理、安全教...
- Dynamics.js – 创建逼真的物理动画的 JS 库
-
Dynamics.js是一个用于创建物理动画JavaScript库。你只需要把dynamics.js引入你的页面,然后就可以激活任何DOM元素的CSS属性动画,也可以结合SVG使...
- Vue3 神级工具:终于可以实现打字的动画效果了!
-
Typed.js是一个轻量级的JavaScript库,用于在网页上实现打字机动画效果。它支持自定义打字速度、循环模式、回调函数等,非常适合用于动态展示标语、代码片段或交互式文本效果。核心特性打字...
- 创建酷炫动画效果的10个JavaScript库
-
Dynamics.js是设计基于物理规律的动画的重要JavaScript库。它可以赋予生命给所有包含CSS和SVG属性的DOM(文本对象模型)元素,换句话说,Dynamics.js适用于所有Java...
- 《速度与激情》动画剧首曝剧照,12月26日奈飞上线
-
新京报讯11月19日,《速度与激情》动画剧《速度与激情:间谍赛车手》发布首批剧照,并宣布将于12月26日在奈飞上线。该剧由范·迪塞尔担任制片人,他的女儿SimiliceDiesel加盟配音。此外,...
- 一周热门
- 最近发表
- 标签列表
-
- 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 轮廓宽度 (31)
- CSS 谷歌字体 (33)
- CSS 链接 (31)
- CSS 定位 (31)
- CSS 图片库 (32)
- CSS 图像精灵 (31)
- SVG 文本 (32)
- 时钟启动 (33)
- HTML 游戏 (34)
- JS Loop For (32)