简体   繁体   English

如何转换SVG路径

[英]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: 我的图像:

How my tree with path look like 路径上的树怎么样

What happen when I expand tree 当我展开树时会发生什么

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM