- 点击对应按钮可生成对应的图形
- 按shift键可生成各图形对应的特殊图形
- 此文件目前有:圆形,(圆角)矩形,直线,折线,铅笔,等腰三角形
此svg绘制功能是在一个html文件中实现,并未对函数进行封装
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制svg图</title>
<style>
#svgCanvas {
border: 1px solid #000;
}
.symble {
background-color: rgb(71, 171, 221);
color: white;
padding: 1px
}
.myButton:hover {
background-color: #069b4c;
color: white;
}
</style>
</head>
<body>
<button id="chooseTool" onclick="cancleChoose()" class="symble">选择工具</button>
<button class="myButton" onclick="getEllipise()">圆形工具</button>
<button class="myButton" onclick="getRectangle(false)">矩形工具</button>
<button class="myButton" onclick="getRectangle(true)">圆角矩形工具</button>
<button class="myButton" onclick="getLine()">直线工具</button>
<button class="myButton" onclick="getPolyline()">折线工具</button>
<button class="myButton" onclick="getPencile()">铅笔工具</button>
<button class="myButton" onclick="getISOStriangle()">等腰三角形工具</button>
<br>
<svg id="svgCanvas" width="800" height="600">
</svg>
<script>
const svg = document.getElementById("svgCanvas");
let isDrawing = false;//是否选中绘制工具
let newPath = "";//路径标签
let isShiftPressed = false; //是否按下了shift键
// 以数字或字母枚举,暂定 1:矩形,2:圆形,3:直线,4:折线,5:铅笔,6:等腰三角形工具
let selectedShape; //选中的形状
// *********************************************************
let click_coordinateX = null; //开始绘制ellipse的起点
let click_coordinateY = null;
let distanceX = 0; //鼠标移动距离
let distanceY = 0; //鼠标移动距离
let isRounded = true;//是否有圆角
// ****折线相关变量
let pathString = ''; //绘制路径
let currentPath;
let isOver = false;//是否点击了右键完成当前折线的绘制
let strench_Path = ""; //辅助路径标签
let strench_currentPath;
let currentPath_p; //铅笔相关
let pathString_p = ''; //绘制路径
let startX = 0;//等腰三角形始点坐标
let startY = 0;
// *****************************以下函数后续可进行封装
function getRectangle(round) { //用按键模拟选中操作工具
console.log("选中矩形工具");
isDrawing = true;
selectedShape = 1;
isRounded = round; //选中的是否为圆角矩形
isOver = true;
};
function getEllipise() { //用按键模拟选中操作工具
console.log("选中圆形工具");
isDrawing = true;
selectedShape = 2;
isOver = true;
};
function getLine() { //用按键模拟选中操作工具
console.log("选中线段工具");
isDrawing = true;
selectedShape = 3;
isOver = true;
};
function getPolyline() { //用按键模拟选中操作工具
console.log("选中折线工具");
isDrawing = true;
selectedShape = 4;
isOver = true;
};
function getPencile() { //用按键模拟选中操作工具
console.log("选中铅笔工具");
isDrawing = true;
selectedShape = 5;
isOver = true;
};
function getISOStriangle() { //用按键模拟选中操作工具
console.log("选中ISOS三角形工具");
isDrawing = true;
selectedShape = 6;
isOver = true;
};
function cancleChoose() { // 用按键模拟取消选中工具的操作
console.log("cancle");
isDrawing = false;
selectedShape = null;
isOver = true;
};
// *********************************************************
svg.addEventListener("mousedown", function (event) {
if (!isDrawing) { return } //若选中的不是当前绘图工具,则不执行以下操作
if (event.button === 2) { //若鼠标右键被按下,则不绘制图形
isDrawing = false; //结束绘制
}
const { offsetX, offsetY } = event;
if (selectedShape === 2) {
// 初始化添加标签----圆形
newPath = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
click_coordinateX = offsetX;
click_coordinateY = offsetY;
newPath.setAttribute("cx", offsetX);
newPath.setAttribute("cy", offsetY);
newPath.setAttribute("stroke", "black");
newPath.setAttribute("fill", "#289");
} else if (selectedShape == 1) {
// 初始化添加标签----矩形
newPath = document.createElementNS("http://www.w3.org/2000/svg", "rect");
// 若为圆角矩形,则添加
if (isRounded) {
newPath.setAttribute("rx", 20);
newPath.setAttribute("ry", 20);
}
click_coordinateX = offsetX;
click_coordinateY = offsetY;
newPath.setAttribute("x", offsetX);
newPath.setAttribute("y", offsetY);
newPath.setAttribute("stroke", "black");
newPath.setAttribute("fill", "#619");
} else if (selectedShape === 3) {
// 初始化添加标签----线段
newPath = document.createElementNS("http://www.w3.org/2000/svg", "line");
click_coordinateX = offsetX;
click_coordinateY = offsetY;
newPath.setAttribute("x1", offsetX);
newPath.setAttribute("y1", offsetY);
newPath.setAttribute("x2", offsetX);
newPath.setAttribute("y2", offsetY);
newPath.setAttribute("stroke", "black");
newPath.setAttribute("stroke-width", "2");
} else if (selectedShape === 4) {
if (event.button === 2) {
// 右键点击结束当前折线路径绘制
if (currentPath) {
isOver = true; //右键的话当前路径完结
}
// ************************************折线相关
// 右键点击后删除辅助标签
var element = document.getElementById("assistPath"); // 获取需要移除的元素
if (element) {
element.parentNode.removeChild(element);
}
// 如果只有一对坐标点时,清除对应的路径标签
if (pathString.trim().split(" ").length === 1) {
if (newPath) {
svg.removeChild(newPath);
}
pathString = "";
return;
}
return;
}
// debugger
// 初始化添加标签或当前绘制完成添加下一个标签
if (pathString === '' || isOver) {
newPath = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
isOver = false;
pathString = "";
}
pathString += `${offsetX},${offsetY} `;
newPath.setAttribute("points", pathString);
newPath.setAttribute("stroke", "black");
newPath.setAttribute("fill", "none");
newPath.setAttribute("class", "wasChecked");
currentPath = newPath;
svg.appendChild(newPath);
// 获取需要移除的元素
var element = document.getElementById("assistPath");
if (element) {
element.parentNode.removeChild(element);
}
// 创建辅助路径标签
strench_Path = document.createElementNS("http://www.w3.org/2000/svg", "line");
strench_Path.setAttribute("x1", offsetX);
strench_Path.setAttribute("y1", offsetY);
strench_Path.setAttribute("x2", offsetX);
strench_Path.setAttribute("y2", offsetY);
strench_Path.setAttribute("stroke", "#ffb27c");
strench_Path.setAttribute("fill", "none");
strench_Path.setAttribute("id", "assistPath");
strench_currentPath = strench_Path;
svg.appendChild(strench_Path);
} else if (selectedShape === 5) {
pathString_p = `M ${offsetX} ${offsetY}`;
newPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
newPath.setAttribute("d", pathString_p);
newPath.setAttribute("stroke", "black");
newPath.setAttribute("fill", "none");
currentPath_p = newPath;
} else if (selectedShape === 6) {
startX = offsetX;
startY = offsetY;
const pathString = `M ${startX} ${startY}`;
currentPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
currentPath.setAttribute("d", pathString);
currentPath.setAttribute("stroke", "black");
currentPath.setAttribute("fill", "none");
svg.appendChild(currentPath);
}
// 折线工具若没有右键退出绘制模式,切换到其他绘制图形时,会有折线的辅助直线保留,以下为直接选择其他工具时,清除辅助直线的
var element = document.getElementById("assistPath"); // 获取需要移除的元素
if (selectedShape !== 4 && element) {
// ************************************折线相关
// 右键点击后删除辅助标签
if (element) {
element.parentNode.removeChild(element);
}
// 如果只有一对坐标点时,清除对应的路径标签
if (pathString.trim().split(" ").length === 1) {
if (newPath) {
svg.removeChild(newPath);
}
pathString = "";
return;
}
}
svg.appendChild(newPath);
});
svg.addEventListener("mousemove", function (event) {
if (!isDrawing) return;
const { offsetX, offsetY } = event;
// 折线相关**********其对应的辅助路径要出现
if (selectedShape == 4) {
// 画辅助路径
if (strench_currentPath) {
strench_Path.setAttribute("x2", offsetX);
strench_Path.setAttribute("y2", offsetY);
}
}
if (event.buttons !== 1) { return }; //鼠标左键被按下
// console.log("E", event);
// 计算鼠标移动的距离获取获取长轴和短轴的值
let rx = 0
let ry = 0
rx = Math.abs(click_coordinateX - offsetX);
ry = Math.abs(click_coordinateY - offsetY);
// 获取点击点到鼠标移动的距离
let r_x;
let r_y;
r_x = Math.abs(click_coordinateX - rx);
r_y = Math.abs(click_coordinateY - ry);
if (selectedShape == 2) {
// 若键盘按下shift键,则长短轴相等
if (isShiftPressed) {
rx = ry = Number(rx) > Number(ry) ? rx : ry;
}
newPath.setAttribute("rx", rx);
newPath.setAttribute("ry", ry);
} else if (selectedShape == 1) {
// 若键盘按下shift键,则长短轴相等
if (isShiftPressed) {
rx = ry = Number(rx) > Number(ry) ? rx : ry;
}
newPath.setAttribute("width", rx);
newPath.setAttribute("height", ry);
// 重新获取 rect 标签的 x,y 值
//0、 矩形在点击点的右下角时,使用原始点击点作为 x,y的值,其他情况如下
if (click_coordinateX > offsetX && click_coordinateY > offsetY) { //1、在点击点的左上角
newPath.setAttribute("x", offsetX);
newPath.setAttribute("y", offsetY);
} else if (click_coordinateX > offsetX) { //2、在点击点的左下角
newPath.setAttribute("x", r_x);
} else if (click_coordinateY > offsetY) { //3、在点击点的右上角
newPath.setAttribute("y", r_y);
}
} else if (selectedShape == 3) {
// 若键盘按下shift键,则线段情况为,横线,竖线,45°角的线段
if (isShiftPressed) {
distanceX = Math.abs(offsetX - click_coordinateX);
distanceY = Math.abs(offsetY - click_coordinateY);
let c = distanceX * distanceX + distanceY * distanceY;
let angles = calculateAngleWithXAxis(click_coordinateX, click_coordinateY, offsetX, offsetY)
console.log("ag", angles);
if ((angles > -30 && angles < 30) || (angles < -150 || angles > 150)) {
newPath.setAttribute("x2", offsetX);
newPath.setAttribute("y2", click_coordinateY);
} else if ((angles < -60 && angles > -120) || (angles > 60 && angles < 120)) {
newPath.setAttribute("x2", click_coordinateX);
newPath.setAttribute("y2", offsetY);
} else if ((angles >= 120 && angles < 150)) {
let side = distanceY > distanceX ? distanceY : distanceX;
newPath.setAttribute("x2", click_coordinateX - side);
newPath.setAttribute("y2", click_coordinateY + side);
} else if ((angles > -150 && angles < -120)) {
let side = distanceY > distanceX ? distanceY : distanceX;
newPath.setAttribute("x2", click_coordinateX - side);
newPath.setAttribute("y2", click_coordinateY - side);
} else if ((angles > -60 && angles < -30)) {
let side = distanceY > distanceX ? distanceY : distanceX;
newPath.setAttribute("x2", click_coordinateX + side);
newPath.setAttribute("y2", click_coordinateY - side);
} else if ((angles > 30 && angles < 60)) {
let side = distanceY > distanceX ? distanceY : distanceX;
newPath.setAttribute("x2", click_coordinateX + side);
newPath.setAttribute("y2", click_coordinateY + side);
}
} else {
// 没有按下shift键的线段情况
newPath.setAttribute("x2", offsetX);
newPath.setAttribute("y2", offsetY);
}
} else if (selectedShape == 5) {
if (currentPath_p) {
pathString_p += ` L ${offsetX} ${offsetY}`;
currentPath_p.setAttribute("d", pathString_p);
}
} else if (selectedShape === 6) {
let endX = offsetX;
let endY = offsetY;
if (event.shiftKey) {
// 如果按下 Shift 键,则将三角形转换为等边三角形
const sideLength = Math.min(Math.abs(endX - startX), Math.abs(endY - startY));
endX = startX + (endX > startX ? sideLength : -sideLength);
endY = startY + (endY > startY ? sideLength : -sideLength);
}
const path = `M ${startX} ${startY} L ${endX} ${endY} L ${2 * startX - endX} ${endY} Z`;
currentPath.setAttribute("d", path);
}
});
svg.addEventListener("mouseup", function (event) {
const { offsetX, offsetY } = event;
const childNodes = Array.from(svg.childNodes);
// console.log("selectedShape", selectedShape);
childNodes.forEach(node => {
// console.log("node", node);
if (node.nodeName === "ellipse") {
let rx = node?.getAttribute("rx");
let ry = node?.getAttribute("ry");
// 长轴或短轴不存在或其值小于1时,移除此dom元素
if ((!rx || !ry) || Number(rx) < 1 || Number(ry) < 1) {
svg.removeChild(node);
}
} else if (node.nodeName === "rect") {
let w = node?.getAttribute("width");
let h = node?.getAttribute("height");
// 长轴或短轴不存在或其值小于1时,移除此dom元素
if ((!w || !h) || Number(w) < 1 || Number(h) < 1) {
svg.removeChild(node);
}
} else if (node.nodeName === "line" && selectedShape == 3) {
let dx = Math.abs(node.getAttribute("x2") - node.getAttribute("x1"));
let dy = Math.abs(node.getAttribute("y2") - node.getAttribute("y1"));
// 线段的位移小于2时,移除对应的dom
if (dx < 2 && dy < 2) {
svg.removeChild(node);
}
} else if (node.nodeName === "path") {
// 若路径中没有包含L,则说明只是进行了点击,并未移动生成路径,所以删除对应的标签
let d = node.getAttribute("d")
if (!d.includes("L")) {
svg.removeChild(node);
}
}
});
})
svg.addEventListener("contextmenu", function (event) {
event.preventDefault();
});
// 监听键盘按下事件
document.addEventListener('keydown', function (event) {
// console.log('Key pressed:', event);
if (event.key === 'Shift') { // 检查特定按键
isShiftPressed = true;
}
});
// 监听键盘释放事件
document.addEventListener('keyup', function (event) {
if (event.key === 'Shift') {
isShiftPressed = false;
}
});
// 计算线段与X轴夹角
function calculateAngleWithXAxis(x1, y1, x2, y2) {
let angleRadians = Math.atan2(y2 - y1, x2 - x1);
let angleDegrees = angleRadians * (180 / Math.PI);
return angleDegrees;
}
</script>
</body>
</html>