[英]How to transform SVG Path
I'm using SVG path to draw connection lines between two Angular Material tree components. 我正在使用SVG路径在两个Angular Material树组件之间绘制连接线。 I have a problem because when I expand tree nodes the path moves up or down but I want it to stay in the same position. 我有一个问题,因为当我展开树节点时,路径向上或向下移动但我希望它保持在相同的位置。 And when I collapse tree nodes I want to move paths from child nodes to parent node. 当我折叠树节点时,我想将路径从子节点移动到父节点。
I have tried to transform: translate or matrix but I didn't know how to calculate values. 我试图转换:翻译或矩阵,但我不知道如何计算值。
Should I use transform or edit "d" attribute of path?? 我应该使用转换或编辑路径的“d”属性吗?
This is how I create svg: 这是我创建svg的方式:
createSVG() {
let svgContainer = document.getElementById('svg-main-container');
this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
this.svg.setAttribute('id', 'svg-canvas');
this.svg.setAttribute('style', `position:absolute;left:0;top:0;display:inline-block;height:100%;width:100%`);
this.svg.setAttribute('viewBox', '0 0 100 100');
this.svg.setAttribute("preserveAspectRatio", "none");
this.svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
svgContainer.appendChild(this.svg);
this.svgService.svg = this.svg;
return this.svg;
}
This is how I draw connection path: 这是我绘制连接路径的方式:
drawConnector(a,b){
let path = document.createElementNS("http://www.w3.org/2000/svg", "path");
let d = `M${a.x_left},${a.y_left} C50,${a.y_left} 50 ${b.y_right} ${b.x_right} ${b.y_right}`;
path.setAttributeNS(null, "d", d);
path.setAttributeNS(null, "fill", "none");
path.setAttributeNS(null, "stroke", "#555");
path.setAttributeNS(null, "stroke-width", "1.5px");
path.setAttributeNS(null, "vector-effect", "non-scaling-stroke");
path.setAttribute("id", this.svgService.draggedElementId);
path.setAttribute("class", this.svgService.draggedElementId);
this.svgService.svg.appendChild(path);
}
How I connect elements: 我如何连接元素:
connectDivs(leftId, rightId, color, tension) {
leftId = this.svgService.draggedElementId
rightId = this.svgService.droppedElementId
this.svgService.connections.push({leftId,rightId});
let svgContainer = document.getElementById('svg-component');
let mainBox = svgContainer.getBoundingClientRect();
let points = [];
//left element
let left = document.getElementById(leftId);
let leftPosition = left.getBoundingClientRect()
let x_left = this.mapCoordinates(leftPosition.left - mainBox.left + leftPosition.width/2, mainBox.left, mainBox.left + mainBox.width, 0, 100);
let y_left = this.mapCoordinates(leftPosition.top - mainBox.top + leftPosition.height/2, mainBox.top, mainBox.top + mainBox.height, 0, 100);
points.push({x_left, y_left});
//right element
let right = document.getElementById(rightId);
let rightPosition = right.getBoundingClientRect()
let x_right = this.mapCoordinates(rightPosition.left - mainBox.left + rightPosition.width/2, mainBox.left, mainBox.left + mainBox.width, 0, 100);
let y_right = this.mapCoordinates(rightPosition.top - mainBox.top + rightPosition.height/2, mainBox.top, mainBox.top + mainBox.height, 0, 100);
points.push({x_right, y_right});
this.drawConnector(points[0], points[1]);
}
And how I tried to transform path when tree is expanded: 当树扩展时我如何尝试转换路径:
expandLines(node){
let nodeElement = document.getElementById(node.id);
let svgContainer = document.getElementById('svg-main-container');
let svgBox = svgContainer.getBoundingClientRect();
let nodePosition = nodeElement.getBoundingClientRect() //position of collapsed/expanded tree element
let x_node = this.mapCoordinates(nodePosition.left - svgBox.left + nodePosition.width/2, svgBox.left, svgBox.left + svgBox.width, 0, 100);
let y_node = this.mapCoordinates(nodePosition.top - svgBox.top + nodePosition.height/2, svgBox.top, svgBox.top + svgBox.height, 0, 100);
let svgCanvas = document.getElementById('svg-canvas');
let svgCanvasBox = svgCanvas.getBoundingClientRect();
let path = svgCanvas.getElementsByClassName(node.id)[0];
let pathPosition = path.getBoundingClientRect();
let x_path = this.mapCoordinates(pathPosition.left - svgCanvasBox.left + pathPosition.width/2, svgCanvasBox.left, svgCanvasBox.left + svgCanvasBox.width, 0, 100);
let y_path = this.mapCoordinates(pathPosition.top - svgCanvasBox.top + pathPosition.height/2, svgCanvasBox.top, svgCanvasBox.top + svgCanvasBox.height, 0, 100);
let trans_x = x_path + x_node;
let trans_y = y_path - y_node;
//path.setAttribute('transform',`translate(${trans_x}, ${trans_y})`);
path.setAttribute('transform', `matrix(1,0,0,1,${trans_x},${trans_y})`);
}
mapCoordinates(n, a, b, _a, _b){
let d = b - a;
let _d = _b - _a;
let u = _d / d;
return _a + n * u;
}
Images of what I have: 我的图像:
You are combining a viewBox
attribute with a preserveAspectRatio="none"
. 您正在将viewBox
属性与preserveAspectRatio="none"
组合在一起。 This has the effect that the <svg>
element, which is set always to fill the height of the containing element, constantly re-scales the meaning of all coordinates inside the SVG if the size of the container changes. 这样,如果容器的大小发生变化, <svg>
元素(总是设置为填充包含元素的高度)会不断地重新调整SVG内部所有坐标的含义。
When often the advice is to add a viewBox, in your special case, leaving off these two attributes will actually help. 通常建议添加一个viewBox,在你的特殊情况下,省去这两个属性实际上会有所帮助。 The coordinate system of the <svg>
element itself (the "initial viewport") and the coordinate system of its content (the "initial user coordinate system") are two different entities : <svg>
元素本身的坐标系(“初始视口”)及其内容的坐标系(“初始用户坐标系”)是两个不同的实体 :
Each SVG viewport generates a viewport coordinate system and a user coordinate system, initially identical. 每个SVG视口生成一个视口坐标系和一个最初相同的用户坐标系。 Providing a
viewBox
on a viewport's element transforms the user coordinate system relative to the viewport coordinate system... 在视口元素上提供viewBox
相对于视口坐标系转换用户坐标系...
Only without a viewBox
attribute, the two coordinate systems coincide even when the <svg>
element changes its size. 只有没有viewBox
属性,即使<svg>
元素更改其大小,两个坐标系也会重合。
When you have coordinates that are related to content outside the SVG, like your a, b
parameters are, they only have a chance to remain meaningfull when the tree size changes if there is no coordinate transformation inbetween. 当您拥有与SVG之外的内容相关的坐标时,就像a, b
参数一样,如果树之间没有坐标转换,它们只有在树大小改变时才有意义。
Your code does some coordinate transformation by hand to get from screen viewport coordinates to a supposed 100 * 100 viewBox. 您的代码手动进行一些坐标转换,以从屏幕视口坐标到假设的100 * 100 viewBox。 Remove all that. 删除所有。 What you need is the relative vertical positions of your connected items to the <svg>
element. 您需要的是连接项与<svg>
元素的相对垂直位置。 Horizontally, you only go to the sides of your <svg>
: 水平方向,您只能转到<svg>
的两侧:
connectDivs(leftId, rightId, color, tension) {
leftId = this.svgService.draggedElementId
rightId = this.svgService.droppedElementId
this.svgService.connections.push({leftId,rightId});
let svgContainer = document.getElementById('svg-component');
let mainBox = svgContainer.getBoundingClientRect();
let points = [];
//left element
let left = document.getElementById(leftId);
let leftPosition = left.getBoundingClientRect();
// you always want the left side of the <svg> element
let x_left = 0;
let y_left = leftPosition.top - mainBox.top + leftPosition.height/2;
points.push({x_left, y_left});
//right element
let right = document.getElementById(rightId);
let rightPosition = right.getBoundingClientRect();
// the right side of the <svg> element
let x_right = mainBox.width;
let y_right = rightPosition.top - mainBox.top + rightPosition.height/2;
this.drawConnector(points[0], points[1]);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.