前端:SVG绘制流程图
效果
代码
html代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>SVG流程图示例</title><style>/* CSS 样式 */</style><script src="js/index.js"></script> <!-- 引入外部JS文件 -->
</head><body><svg id="workflow-svg" width="1000" height="800"><!-- SVG图形内容... --></svg></body></html>
js/index.js
// 定义矩形框
class FlowChartShape {// 定义一个类的构造函数。svgElement容纳新创建的矩形框、initialX,initialY分别代表矩形框左上角的初始X轴和Y轴坐标、width和height:这两个参数表示矩形框的宽度和高度constructor(svgElement, initialX, initialY, width, height) {//是在当前类的实例(FlowChartShape)内部设置一个成员变量。意味着每个FlowChartShape实例都将与特定的SVG元素关联起来this.svgElement = svgElement;// 创建一个新的SVG < rect > 元素(图形)。this.rectElement = document.createElementNS("http://www.w3.org/2000/svg", 'rect');// 设定矩形元素在其父SVG容器中的X坐标位置this.rectElement.setAttribute('x', initialX);// 设定矩形元素在其父SVG容器中的y坐标位置this.rectElement.setAttribute('y', initialY);// 设定矩形元素在其父SVG容器中的宽度this.rectElement.setAttribute('width', width);// 设定矩形元素在其父SVG容器中的高度this.rectElement.setAttribute('height', height);// 设置矩形的圆角半径设置为10个单位this.rectElement.setAttribute('rx', 10);// 设置当前SVG矩形元素的内部填充颜色为纯白色。this.rectElement.setAttribute('fill', '#fff');// 设置了矩形元素的描边颜色为黑色this.rectElement.setAttribute('stroke', '#1d6ee7');// 矩形元素的描边宽度为2个单位this.rectElement.setAttribute('stroke-width', 2);// 创建了一个新的SVG文本元素。let textElement = document.createElementNS("http://www.w3.org/2000/svg", 'text');// 'x' 属性设置文本元素在水平方向上的起始位置:为矩形容器的起始x坐标加上宽度的一半textElement.setAttribute('x', initialX + width / 2);// 'y' 属性设置文本元素在竖直方向上的起始位置:为矩形容器的起始y坐标加上高度的一半textElement.setAttribute('y', initialY + height / 2);// 设置SVG文本元素的对齐方式:居中textElement.setAttribute('text-anchor', 'middle'); // 这会让文本水平居中textElement.setAttribute('dominant-baseline', 'middle'); // 竖直居中,在某些浏览器中可能需要其他值,比如 'central'// 将先前创建的SVG矩形元素(this.rectElement) 添加为 this.svgElement 的子元素this.svgElement.appendChild(this.rectElement);// 将先前创建的SVG矩形元素(this.textElement) 添加为 this.svgElement 的子元素this.svgElement.appendChild(textElement);// 将文本元素引用赋值给了类的成员变量 this.textElement,这样后续可以通过 this.textElement 直接访问和操作这个文本元素,比如设置文本内容、更改样式等。this.textElement = textElement;}// 设置SVG文本元素的内容setText(textContent) {this.textElement.textContent = textContent;}// 添加创建和连接直线箭头的方法createArrow(x1, y1, x2, y2, color = '#1d6ee7', strokeWidth = 2) {// 创建的SVG元素类型——这里是线段元素const line = document.createElementNS("http://www.w3.org/2000/svg", 'line');// 为刚创建的SVG线段元素设置 x1 属性,表示线段起点的X坐标,这里的 x1 是一个变量,存储了所需的数值。line.setAttribute('x1', x1);// 为线段元素设置 y1 属性,表示线段起点的Y坐标,这里的 y1 是一个变量,存储了所需的数值。line.setAttribute('y1', y1);// 为线段元素设置 x2 属性,表示线段终点的X坐标,这里的 x2 是一个变量,存储了所需的数值。line.setAttribute('x2', x2);// 为线段元素设置 y2 属性,表示线段终点的Y坐标,这里的 y2 是一个变量,存储了所需的数值。line.setAttribute('y2', y2);// 设置线段的颜色line.setAttribute('stroke', color);//设置线段的粗细line.setAttribute('stroke-width', strokeWidth);// 创建箭头头部// 创建一个名为 arrowHead 的SVG多边形元素,这里是创建一个三角形作为箭头头部const arrowHead = document.createElementNS("http://www.w3.org/2000/svg", 'polygon');// 计算向量的差分和长度//计算线段从起点 x1 到终点 x2 在X轴上的位移。const dx = x2 - x1;//计算线段在Y轴上的位移。const dy = y2 - y1;//计算线段的长度(欧几里得距离)const len = Math.sqrt(dx * dx + dy * dy);//计算线段的方向角,即从起点指向终点的角度,使用 Math.atan2 函数得到弧度值const angle = Math.atan2(dy, dx);//定义箭头头部的大小,这是一个常量,用来决定箭头三角形两边的长度const arrowHeadSize = 10;// 计算箭头三角形的两个顶点坐标//根据角度减去π/6(30度),计算箭头左侧顶点相对于线段终点的X坐标。const headX1 = x2 - arrowHeadSize * Math.cos(angle - Math.PI / 6);// 计算箭头左侧顶点相对于线段终点的Y坐标。const headY1 = y2 - arrowHeadSize * Math.sin(angle - Math.PI / 6);// 根据角度加上π/6(30度),计算箭头右侧顶点相对于线段终点的X坐标。const headX2 = x2 - arrowHeadSize * Math.cos(angle + Math.PI / 6);// 计算箭头右侧顶点相对于线段终点的Y坐标。const headY2 = y2 - arrowHeadSize * Math.sin(angle + Math.PI / 6);// 设置SVG多边形元素的points属性,该属性接受一系列顶点坐标,此处定义了一个等腰三角形作为箭头头部,三个顶点分别为线段终点和刚刚计算出的两侧顶点。arrowHead.setAttribute('points', [`${x2},${y2}`, `${headX1},${headY1}`, `${headX2},${headY2}`].join(' '));//设置箭头头部多边形的填充颜色,使其与线段颜色一致。arrowHead.setAttribute('fill', color);//将之前创建的线段元素添加到一个假设存在的SVG容器元素 this.svgElement 中。this.svgElement.appendChild(line);//将创建好的箭头头部(多边形)元素也添加到相同的SVG容器元素中,这样就形成了一个带有箭头的线段图形。this.svgElement.appendChild(arrowHead);}//定义了一个名为connectTo的方法,用于连接两个SVG图形connectTo(otherShape, startAnchor = { side: 'right', verticalAlign: 'center' }, endAnchor = { side: 'left', verticalAlign: 'center' }) {const myBBox = this.rectElement.getBBox();const otherBBox = otherShape.rectElement.getBBox();let startX, startY, endX, endY;switch (startAnchor.side) {case 'left':startX = myBBox.x;startY = startAnchor.verticalAlign === 'top' ? myBBox.y : myBBox.y + myBBox.height / 2;break;case 'right':startX = myBBox.x + myBBox.width;startY = startAnchor.verticalAlign === 'top' ? myBBox.y : myBBox.y + myBBox.height / 2;break;case 'top':startX = startAnchor.horizontalAlign === 'left' ? myBBox.x : myBBox.x + myBBox.width / 2;startY = myBBox.y;break;case 'bottom':startX = startAnchor.horizontalAlign === 'left' ? myBBox.x : myBBox.x + myBBox.width / 2;startY = myBBox.y + myBBox.height;break;default: // 默认为中心点startX = myBBox.x + myBBox.width / 2;startY = myBBox.y + myBBox.height / 2;break;}switch (endAnchor.side) {case 'left':endX = otherBBox.x;endY = endAnchor.verticalAlign === 'top' ? otherBBox.y : otherBBox.y + otherBBox.height / 2;break;case 'right':endX = otherBBox.x + otherBBox.width;endY = endAnchor.verticalAlign === 'top' ? otherBBox.y : otherBBox.y + otherBBox.height / 2;break;case 'top':endX = endAnchor.horizontalAlign === 'left' ? otherBBox.x : otherBBox.x + otherBBox.width / 2;endY = otherBBox.y;break;case 'bottom':endX = endAnchor.horizontalAlign === 'left' ? otherBBox.x : otherBBox.x + otherBBox.width / 2;endY = otherBBox.y + otherBBox.height;break;default: // 默认为中心点endX = otherBBox.x + otherBBox.width / 2;endY = otherBBox.y + otherBBox.height / 2;break;}this.createArrow(startX, startY, endX, endY);}// 新增setLink方法,处理超链接逻辑setLink(url) {const linkElement = document.createElementNS("http://www.w3.org/2000/svg", 'a');linkElement.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', url);// 如果矩形框和文本不在同一个父节点下,或者不在linkElement内if (this.rectElement.parentNode !== linkElement && this.textElement.parentNode !== linkElement) {// 移除它们原先所在的位置this.rectElement.parentNode.removeChild(this.rectElement);this.textElement.parentNode.removeChild(this.textElement);// 将矩形和文本都添加到链接元素内部linkElement.appendChild(this.rectElement);linkElement.appendChild(this.textElement);// 确保链接元素被添加到SVG容器内this.svgElement.appendChild(linkElement);// 更新类的成员变量引用为链接元素this.linkElement = linkElement;} else if (this.linkElement) {// 如果linkElement已经存在但href需要更新this.linkElement.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', url);} else {// 如果linkElement不存在但在同一父节点下,可以直接将它们包裹进新的linkElementthis.rectElement.parentNode.insertBefore(linkElement, this.rectElement);linkElement.appendChild(this.rectElement);linkElement.appendChild(this.textElement);this.linkElement = linkElement;}}
}// 示例:创建三个矩形框并连接它们
window.onload = function () {var svgElement = document.getElementById('workflow-svg');// 初始化三个矩形框并设置链接(x轴,y轴,宽,高)let shape1Ref = new FlowChartShape(svgElement, 50, 150, 120, 40);let shape2Ref = new FlowChartShape(svgElement, 250, 150, 120, 40);let shape3Ref = new FlowChartShape(svgElement, 450, 150, 120, 40);let shape4Ref = new FlowChartShape(svgElement, 650, 150, 120, 40);let shape5Ref = new FlowChartShape(svgElement, 350, 50, 120, 40);let shape6Ref = new FlowChartShape(svgElement, 250, 250, 120, 40);let shape7Ref = new FlowChartShape(svgElement, 450, 250, 120, 40);let shape8Ref = new FlowChartShape(svgElement, 650, 250, 120, 40);let shape9Ref = new FlowChartShape(svgElement, 850, 250, 120, 40);let shape10Ref = new FlowChartShape(svgElement, 850, 350, 120, 40);let shape11Ref = new FlowChartShape(svgElement, 850, 450, 120, 40);let shape12Ref = new FlowChartShape(svgElement, 350, 350, 120, 40);let shape13Ref = new FlowChartShape(svgElement, 550, 350, 120, 40);setTimeout(() => {//设置名称shape1Ref.setText('销售订单');shape2Ref.setText('报价申请');shape3Ref.setText('报价单签核');shape4Ref.setText('报价单回复');shape5Ref.setText('报价单修改');shape6Ref.setText('订单建立');shape7Ref.setText('订单审核');shape8Ref.setText('订单出货');shape9Ref.setText('销售退货');shape10Ref.setText('出货对账');shape11Ref.setText('出货对账取消');shape12Ref.setText('订单修改');shape13Ref.setText('订单反审核');// 连接矩形框shape1Ref.connectTo(shape2Ref);shape2Ref.connectTo(shape3Ref);shape3Ref.connectTo(shape4Ref);//2的顶部中点到5的底部中点shape2Ref.connectTo(shape5Ref, {side: 'top',verticalAlign: 'center'}, {side: 'left',verticalAlign: 'center'});// 从shape1Ref的左上角到shape2Ref的右下角:// shape1Ref.connectTo(shape2Ref, {// side: 'left',// verticalAlign: 'top'// }, {// side: 'right',// verticalAlign: 'bottom'// });shape5Ref.connectTo(shape3Ref, {side: 'right',verticalAlign: 'center'}, {side: 'top',verticalAlign: 'center'});shape1Ref.connectTo(shape6Ref);shape6Ref.connectTo(shape7Ref);shape7Ref.connectTo(shape8Ref);shape8Ref.connectTo(shape9Ref);shape9Ref.connectTo(shape10Ref, {side: 'bottom',verticalAlign: 'center'}, {side: 'top',verticalAlign: 'center'});shape10Ref.connectTo(shape11Ref, {side: 'bottom',verticalAlign: 'center'}, {side: 'top',verticalAlign: 'center'});shape7Ref.connectTo(shape13Ref, {side: 'bottom',verticalAlign: 'center'}, {side: 'top',verticalAlign: 'center'});shape13Ref.connectTo(shape12Ref, {side: 'left',verticalAlign: 'center'}, {side: 'right',verticalAlign: 'center'});shape12Ref.connectTo(shape7Ref, {side: 'top',verticalAlign: 'center'}, {side: 'bottom',verticalAlign: 'center'});//添加超链接shape1Ref.setLink('page1.html'); // 为第一个矩形框设置链接到“page1.html”shape2Ref.setLink('page2.html'); // 为第二个矩形框设置链接到“page2.html”shape3Ref.setLink('page3.html'); // 为第三个矩形框设置链接到“page3.html”}, 0);
};
把实现图形的部分写在html页面
html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>SVG流程图示例</title><style>/* CSS 样式 */</style><script src="js/index.js"></script><!-- 引入外部JS文件 --><script>// 示例:创建三个矩形框并连接它们window.onload = function () {var svgElement = document.getElementById('workflow-svg');// 初始化三个矩形框并设置链接(x轴,y轴,宽,高)let shape1Ref = new FlowChartShape(svgElement, 50, 150, 120, 40);let shape2Ref = new FlowChartShape(svgElement, 250, 150, 120, 40);let shape3Ref = new FlowChartShape(svgElement, 450, 150, 120, 40);let shape4Ref = new FlowChartShape(svgElement, 650, 150, 120, 40);let shape5Ref = new FlowChartShape(svgElement, 350, 50, 120, 40);let shape6Ref = new FlowChartShape(svgElement, 250, 250, 120, 40);let shape7Ref = new FlowChartShape(svgElement, 450, 250, 120, 40);let shape8Ref = new FlowChartShape(svgElement, 650, 250, 120, 40);let shape9Ref = new FlowChartShape(svgElement, 850, 250, 120, 40);let shape10Ref = new FlowChartShape(svgElement, 850, 350, 120, 40);let shape11Ref = new FlowChartShape(svgElement, 850, 450, 120, 40);let shape12Ref = new FlowChartShape(svgElement, 350, 350, 120, 40);let shape13Ref = new FlowChartShape(svgElement, 550, 350, 120, 40);setTimeout(() => {//设置名称shape1Ref.setText('销售订单');shape2Ref.setText('报价申请');shape3Ref.setText('报价单签核');shape4Ref.setText('报价单回复');shape5Ref.setText('报价单修改');shape6Ref.setText('订单建立');shape7Ref.setText('订单审核');shape8Ref.setText('订单出货');shape9Ref.setText('销售退货');shape10Ref.setText('出货对账');shape11Ref.setText('出货对账取消');shape12Ref.setText('订单修改');shape13Ref.setText('订单反审核');// 连接矩形框shape1Ref.connectTo(shape2Ref);shape2Ref.connectTo(shape3Ref);shape3Ref.connectTo(shape4Ref);//2的顶部中点到5的底部中点shape2Ref.connectTo(shape5Ref, {side: 'top',verticalAlign: 'center'}, {side: 'left',verticalAlign: 'center'});// 从shape1Ref的左上角到shape2Ref的右下角:// shape1Ref.connectTo(shape2Ref, {// side: 'left',// verticalAlign: 'top'// }, {// side: 'right',// verticalAlign: 'bottom'// });shape5Ref.connectTo(shape3Ref, {side: 'right',verticalAlign: 'center'}, {side: 'top',verticalAlign: 'center'});shape1Ref.connectTo(shape6Ref);shape6Ref.connectTo(shape7Ref);shape7Ref.connectTo(shape8Ref);shape8Ref.connectTo(shape9Ref);shape9Ref.connectTo(shape10Ref, {side: 'bottom',verticalAlign: 'center'}, {side: 'top',verticalAlign: 'center'});shape10Ref.connectTo(shape11Ref, {side: 'bottom',verticalAlign: 'center'}, {side: 'top',verticalAlign: 'center'});shape7Ref.connectTo(shape13Ref, {side: 'bottom',verticalAlign: 'center'}, {side: 'top',verticalAlign: 'center'});shape13Ref.connectTo(shape12Ref, {side: 'left',verticalAlign: 'center'}, {side: 'right',verticalAlign: 'center'});shape12Ref.connectTo(shape7Ref, {side: 'top',verticalAlign: 'center'}, {side: 'bottom',verticalAlign: 'center'});//添加超链接shape1Ref.setLink('page1.html'); // 为第一个矩形框设置链接到“page1.html”shape2Ref.setLink('page2.html'); // 为第二个矩形框设置链接到“page2.html”shape3Ref.setLink('page3.html'); // 为第三个矩形框设置链接到“page3.html”}, 0);};</script>
</head><body><svg id="workflow-svg" width="1000" height="800"><!-- SVG图形内容... --></svg></body></html>
js
增加linkElement.setAttribute('target', '_blank'); // 使链接在新窗口打开
// 定义矩形框
class FlowChartShape {// 定义一个类的构造函数。svgElement容纳新创建的矩形框、initialX,initialY分别代表矩形框左上角的初始X轴和Y轴坐标、width和height:这两个参数表示矩形框的宽度和高度constructor(svgElement, initialX, initialY, width, height) {//是在当前类的实例(FlowChartShape)内部设置一个成员变量。意味着每个FlowChartShape实例都将与特定的SVG元素关联起来this.svgElement = svgElement;// 创建一个新的SVG < rect > 元素(图形)。this.rectElement = document.createElementNS("http://www.w3.org/2000/svg", 'rect');// 设定矩形元素在其父SVG容器中的X坐标位置this.rectElement.setAttribute('x', initialX);// 设定矩形元素在其父SVG容器中的y坐标位置this.rectElement.setAttribute('y', initialY);// 设定矩形元素在其父SVG容器中的宽度this.rectElement.setAttribute('width', width);// 设定矩形元素在其父SVG容器中的高度this.rectElement.setAttribute('height', height);// 设置矩形的圆角半径设置为10个单位this.rectElement.setAttribute('rx', 10);// 设置当前SVG矩形元素的内部填充颜色为纯白色。this.rectElement.setAttribute('fill', '#fff');// 设置了矩形元素的描边颜色为黑色this.rectElement.setAttribute('stroke', '#1d6ee7');// 矩形元素的描边宽度为2个单位this.rectElement.setAttribute('stroke-width', 2);// 创建了一个新的SVG文本元素。let textElement = document.createElementNS("http://www.w3.org/2000/svg", 'text');// 'x' 属性设置文本元素在水平方向上的起始位置:为矩形容器的起始x坐标加上宽度的一半textElement.setAttribute('x', initialX + width / 2);// 'y' 属性设置文本元素在竖直方向上的起始位置:为矩形容器的起始y坐标加上高度的一半textElement.setAttribute('y', initialY + height / 2);// 设置SVG文本元素的对齐方式:居中textElement.setAttribute('text-anchor', 'middle'); // 这会让文本水平居中textElement.setAttribute('dominant-baseline', 'middle'); // 竖直居中,在某些浏览器中可能需要其他值,比如 'central'// 将先前创建的SVG矩形元素(this.rectElement) 添加为 this.svgElement 的子元素this.svgElement.appendChild(this.rectElement);// 将先前创建的SVG矩形元素(this.textElement) 添加为 this.svgElement 的子元素this.svgElement.appendChild(textElement);// 将文本元素引用赋值给了类的成员变量 this.textElement,这样后续可以通过 this.textElement 直接访问和操作这个文本元素,比如设置文本内容、更改样式等。this.textElement = textElement;}// 设置SVG文本元素的内容setText(textContent) {this.textElement.textContent = textContent;}// 添加创建和连接直线箭头的方法createArrow(x1, y1, x2, y2, color = '#1d6ee7', strokeWidth = 2) {// 创建的SVG元素类型——这里是线段元素const line = document.createElementNS("http://www.w3.org/2000/svg", 'line');// 为刚创建的SVG线段元素设置 x1 属性,表示线段起点的X坐标,这里的 x1 是一个变量,存储了所需的数值。line.setAttribute('x1', x1);// 为线段元素设置 y1 属性,表示线段起点的Y坐标,这里的 y1 是一个变量,存储了所需的数值。line.setAttribute('y1', y1);// 为线段元素设置 x2 属性,表示线段终点的X坐标,这里的 x2 是一个变量,存储了所需的数值。line.setAttribute('x2', x2);// 为线段元素设置 y2 属性,表示线段终点的Y坐标,这里的 y2 是一个变量,存储了所需的数值。line.setAttribute('y2', y2);// 设置线段的颜色line.setAttribute('stroke', color);//设置线段的粗细line.setAttribute('stroke-width', strokeWidth);// 创建箭头头部// 创建一个名为 arrowHead 的SVG多边形元素,这里是创建一个三角形作为箭头头部const arrowHead = document.createElementNS("http://www.w3.org/2000/svg", 'polygon');// 计算向量的差分和长度//计算线段从起点 x1 到终点 x2 在X轴上的位移。const dx = x2 - x1;//计算线段在Y轴上的位移。const dy = y2 - y1;//计算线段的长度(欧几里得距离)const len = Math.sqrt(dx * dx + dy * dy);//计算线段的方向角,即从起点指向终点的角度,使用 Math.atan2 函数得到弧度值const angle = Math.atan2(dy, dx);//定义箭头头部的大小,这是一个常量,用来决定箭头三角形两边的长度const arrowHeadSize = 10;// 计算箭头三角形的两个顶点坐标//根据角度减去π/6(30度),计算箭头左侧顶点相对于线段终点的X坐标。const headX1 = x2 - arrowHeadSize * Math.cos(angle - Math.PI / 6);// 计算箭头左侧顶点相对于线段终点的Y坐标。const headY1 = y2 - arrowHeadSize * Math.sin(angle - Math.PI / 6);// 根据角度加上π/6(30度),计算箭头右侧顶点相对于线段终点的X坐标。const headX2 = x2 - arrowHeadSize * Math.cos(angle + Math.PI / 6);// 计算箭头右侧顶点相对于线段终点的Y坐标。const headY2 = y2 - arrowHeadSize * Math.sin(angle + Math.PI / 6);// 设置SVG多边形元素的points属性,该属性接受一系列顶点坐标,此处定义了一个等腰三角形作为箭头头部,三个顶点分别为线段终点和刚刚计算出的两侧顶点。arrowHead.setAttribute('points', [`${x2},${y2}`, `${headX1},${headY1}`, `${headX2},${headY2}`].join(' '));//设置箭头头部多边形的填充颜色,使其与线段颜色一致。arrowHead.setAttribute('fill', color);//将之前创建的线段元素添加到一个假设存在的SVG容器元素 this.svgElement 中。this.svgElement.appendChild(line);//将创建好的箭头头部(多边形)元素也添加到相同的SVG容器元素中,这样就形成了一个带有箭头的线段图形。this.svgElement.appendChild(arrowHead);}//定义了一个名为connectTo的方法,用于连接两个SVG图形connectTo(otherShape, startAnchor = { side: 'right', verticalAlign: 'center' }, endAnchor = { side: 'left', verticalAlign: 'center' }) {const myBBox = this.rectElement.getBBox();const otherBBox = otherShape.rectElement.getBBox();let startX, startY, endX, endY;switch (startAnchor.side) {case 'left':startX = myBBox.x;startY = startAnchor.verticalAlign === 'top' ? myBBox.y : myBBox.y + myBBox.height / 2;break;case 'right':startX = myBBox.x + myBBox.width;startY = startAnchor.verticalAlign === 'top' ? myBBox.y : myBBox.y + myBBox.height / 2;break;case 'top':startX = startAnchor.horizontalAlign === 'left' ? myBBox.x : myBBox.x + myBBox.width / 2;startY = myBBox.y;break;case 'bottom':startX = startAnchor.horizontalAlign === 'left' ? myBBox.x : myBBox.x + myBBox.width / 2;startY = myBBox.y + myBBox.height;break;default: // 默认为中心点startX = myBBox.x + myBBox.width / 2;startY = myBBox.y + myBBox.height / 2;break;}switch (endAnchor.side) {case 'left':endX = otherBBox.x;endY = endAnchor.verticalAlign === 'top' ? otherBBox.y : otherBBox.y + otherBBox.height / 2;break;case 'right':endX = otherBBox.x + otherBBox.width;endY = endAnchor.verticalAlign === 'top' ? otherBBox.y : otherBBox.y + otherBBox.height / 2;break;case 'top':endX = endAnchor.horizontalAlign === 'left' ? otherBBox.x : otherBBox.x + otherBBox.width / 2;endY = otherBBox.y;break;case 'bottom':endX = endAnchor.horizontalAlign === 'left' ? otherBBox.x : otherBBox.x + otherBBox.width / 2;endY = otherBBox.y + otherBBox.height;break;default: // 默认为中心点endX = otherBBox.x + otherBBox.width / 2;endY = otherBBox.y + otherBBox.height / 2;break;}this.createArrow(startX, startY, endX, endY);}// 新增setLink方法,处理超链接逻辑setLink(url) {const linkElement = document.createElementNS("http://www.w3.org/2000/svg", 'a');linkElement.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', url);linkElement.setAttribute('target', '_blank'); // 使链接在新窗口打开// 如果矩形框和文本不在同一个父节点下,或者不在linkElement内if (this.rectElement.parentNode !== linkElement && this.textElement.parentNode !== linkElement) {// 移除它们原先所在的位置this.rectElement.parentNode.removeChild(this.rectElement);this.textElement.parentNode.removeChild(this.textElement);// 将矩形和文本都添加到链接元素内部linkElement.appendChild(this.rectElement);linkElement.appendChild(this.textElement);// 确保链接元素被添加到SVG容器内this.svgElement.appendChild(linkElement);// 更新类的成员变量引用为链接元素this.linkElement = linkElement;} else if (this.linkElement) {// 如果linkElement已经存在但href需要更新this.linkElement.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', url);} else {// 如果linkElement不存在但在同一父节点下,可以直接将它们包裹进新的linkElementthis.rectElement.parentNode.insertBefore(linkElement, this.rectElement);linkElement.appendChild(this.rectElement);linkElement.appendChild(this.textElement);this.linkElement = linkElement;}}
}
相关文章:

前端:SVG绘制流程图
效果 代码 html代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>SVG流程图示例</title><style>/* CSS 样式 */</style><script src"js/index.js"></script…...

【Linux系列】如何确定当前运行的是 RHEL 9 还是 RHEL 8?
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

vscode开发java的插件和配置
推荐插件 .vscode/extensions.json {"recommendations": ["redhat.fabric8-analytics","ms-azuretools.vscode-docker","vscjava.vscode-java-pack","eamodio.gitlens","obkoro1.korofileheader","redhat.j…...

Mysql启动报错:本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止
Mysql启动报错:本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止 文章目录 Mysql启动报错:本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止1. 备份mysql的data文件夹2. 重新构建 Wind…...

WPF程序添加托盘图标
程序添加托盘图标 UI层 //添加handycontrol的引用xmlns:hc"https://handyorg.github.io/handycontrol"//添加NotifyIcon图标 实现单击 双击 二级菜单点击功能<hc:NotifyIconText"通知"Token"Info"><hc:NotifyIcon.ContextMenu><…...

工业4g路由器联网后迅速掉线是什么原因?
工业4G路由器连接上网后迅速掉线可能是由多种因素造成的。以下是一些建议的检查和解决步骤: 1、信号问题: 信号强度:检查工业路由器信号强度指示灯,如果信号弱,尝试移动路由器位置或添加外部天线来增强信号。 网络拥…...

腾讯云4核8G服务器12M带宽646元1年零3个月,4C8G使用场景说明
腾讯云4核8G服务器多少钱?腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月,活动页面 txybk.com/go/txy 活动链接打开如下图所示: 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器,详细配置为:轻量4核…...

java - 读取配置文件
文章目录 1. properties2. XML(1) dom4j(2) XPath 1. properties // 创建properties对象用于读取properties文件Properties properties new Properties();properties.load(new FileReader("src/main/resources/test.properties"));String name properties.getPrope…...

Ubuntu22.04平台编译完美解决问题“error: GLSL 4.5 is not supported.”【GLSL(OpenGL着色器语言)】
GLSL介绍 GLSL(OpenGL着色器语言)是用于编写OpenGL着色器程序的语言。GLSL 4.5 是 GLSL 的一个版本,引入了许多新的特性和改进,旨在提高着色器编程的灵活性和性能。GLSL 4.5 工具通常是用于编写、调试和优化 GLSL 4.5 着色器代码…...

数据结构之搜索二叉树与关联性容器初接触
一、搜索二叉树 1>、前言 1. map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构 2. 二叉搜索树的特性了解,有助于更好的理解map和set的特性。 2>、概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者…...

C语言整数和小数的存储
1.整数在内存中的存储 计算机使用二进制进行存储、运算,整数在内存中存储使用的是二进制补码 1.1原码、反码、补码 整数的2进制表⽰⽅法有三种,即 原码、反码和补码 三种表⽰⽅法均有符号位和数值位两部分,符号位都是⽤0表⽰“正”&am…...

Games101Homework【6】Acceleration structure(Including framework analysis)
Code Analysis: friend: C中友元(友元函数和友元类)的用法和功能_friend class a<b>-CSDN博客 [C:不如Coding](11):友元函数与友元类_哔哩哔哩_bilibili Here is a simple…...

应用运维文档1
统一nginx接入配置指南 Nginx配置规范 1:不带微服务编码上下文至后端,以metadata-ui为例 location段配置信息,location配置中维护微服务编码上下文信息 # app_code: metadata-ui 流水线名称: metadata-ui location ~ ^/metadata-ui/(?P.*) {set $app_code metadata-ui;p…...

手机如何在线制作gif?轻松一键在线操作
现在大家都喜欢使用手机来拍摄记录有趣的事物,但是时间长了手机里的视频越来越多导致手机存储空间不够了,这些视频又不想删除时应该怎么办呢?这个很简单,下面就给大家分享一款不用下载手机就能操作的视频转gif网站-GIF中文网&…...

ChatGPT 在做什么,为什么有效?
原文:What Is ChatGPT Doing … and Why Does It Work? 译者:飞龙 协议:CC BY-NC-SA 4.0 序言 这本简短的书试图从第一原理解释 ChatGPT 是如何工作的。在某种程度上,这是关于技术的故事。但它也是关于科学的故事。以及关于哲学…...

Linux实验2 初步使用shell
一:实验目的 学习Linux下的文件系统结构,了解最基本的Linux下的shell命令操作,例如ls, cd, cat等各种指令操作。 学习vim编辑器的使用方式,学习如何使用ssh连接远程服务器。 二:实验内容 1.请指出下面每…...

甘特图/横道图制作技巧 - 任务组
在甘特图中通过合理的任务分组可以让项目更加清晰,修改也更方便。 列如下面的甘特图一眼不太容易看清楚整体的进度。或者需要把所有的任务整体的延迟或者提前只能这样一个一个的任务调整,就比较麻烦。 通过给任务分组,看这上面整体的进度就…...

Web题记
反序列化补充知识: private变量会被序列化为:\x00类名\x00变量名 protected变量会被序列化为: \x00\*\x00变量名 public变量会被序列化为:变量名web254 这个逻辑不难,自己刚看的时候还奇怪是不是自己哪里想错了,因为…...

学习java第三十六天
Spring 官网列出的 Spring 的 6 个特征: 核心技术 :依赖注入(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换,SpEL。 测试 :模拟对象,…...

0205矩阵分块法-矩阵及其运算-线性代数
文章目录 1 分块矩阵的定义2 分块矩阵的运算(性质)3 按列分块与按行分块 结语 1 分块矩阵的定义 将矩阵A用若干条纵线和横线分成许多个小矩阵,每一个小矩阵称为A的子快,以子块为元素的形式上的矩阵称为分块矩阵。 2 分块矩阵的运算…...

1、java语法入门(找工作版)
文章目录 一、Java简介二、Java常量与变量1、标识符2、关键字3、变量4、类的命名规则5、数据类型6、基本数据类型字面值7、变量的定义与初始化8、ASCII码和Unicode编码9、转义字符10、类型转换11、常量 三、Java运算符1、算术运算符2、赋值运算符3、关系运算符4、逻辑运算符5、…...

arm的状态寄存器
目录 一、arm 的 PSRs二、CPSR2.1 CPSR_cxsf 三、SPSR四、APSR 一、arm 的 PSRs arm 中有很多程序状态寄存器(Program Status Registers,PSRs)用于存储处理器的状态信息,包括 CPSR\SPSR\FPSR\APSR 等: CPSRÿ…...

2024 蓝桥打卡Day34
20240406蓝桥杯备赛 1、学习蓝桥云课省赛冲刺课 【1-手写与思维】【2-递归与递推】2、学习蓝桥云课Java省赛无忧班 【1-语言基础】3、代码练习字符串排序大小写转换 (ccfcsp之前要是学了我就能上200了 啊啊啊啊 错过啊)斐波那契数列 递归解法纸张尺寸问题…...

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第九套
华为海思校园招聘-芯片-数字 IC 方向 题目分享(有参考答案)——第九套 部分题目分享,完整版获取(WX:didadidadidida313,加我备注:CSDN huawei数字芯片题目,谢绝白嫖哈) 单选 1&…...

如何创建虚拟环境打包py文件
Python 项目通常依赖于特定的库和版本。不同的项目可能依赖于相同库的不同版本,这可能导致冲突。使用虚拟环境,你可以为每个项目创建一个独立的 Python 环境,每个环境都有自己的库和版本,从而避免了依赖冲突。 采用虚拟环境打包P…...

CSS 学习笔记 总结
CSS 布局方式 • 表格布局 • 元素定位 • 浮动布局(注意浮动的负效应) • flex布局 • grid布局(感兴趣的可以看下菜鸟教程) 居中设置 元素水平居中 • 设置宽度后,margin设置为auto • 父容器设置text-alig…...
基于Swin Transformers的乳腺癌组织病理学图像多分类
乳腺癌的非侵入性诊断程序涉及体检和成像技术,如乳房X光检查、超声检查和磁共振成像。成像程序对于更全面地评估癌症区域和识别癌症亚型的敏感性较低。 CNN表现出固有的归纳偏差,并且对于图像中感兴趣对象的平移、旋转和位置有所不同。因此,…...

MySQL主从的介绍与应用
mysql主从 文章目录 mysql主从1. 主从简介1.1 主从作用1.2 主从形式 2. 主从复制原理3. 主从复制配置3.1 mysql安装(两台主机安装一致,下面只演示一台主机操作)3.2 mysql主从配置3.2.1 确保从数据库与主数据库里的数据一样3.2.2 在主数据库里…...

pytest中文使用文档----12缓存:记录执行的状态
1. cacheprovider插件 1.1. --lf, --last-failed:只执行上一轮失败的用例1.2. --ff, --failed-first:先执行上一轮失败的用例,再执行其它的1.3. --nf, --new-first:先执行新加的或修改的用例,再执行其它的1.4. --cache…...

【代码随想录】哈希表
文章目录 242.有效的字母异位词349. 两个数组的交集202. 快乐数1. 两数之和454. 四数相加 II383. 赎金信15. 三数之和18. 四数之和 242.有效的字母异位词 class Solution {public boolean isAnagram(String s, String t) {if(snull || tnull || s.length()!t.length()){return …...