【问题标题】:How to transform SVG Path如何转换 SVG 路径
【发布时间】:2019-08-26 08:29:31
【问题描述】:

我正在使用 SVG 路径在两个 Angular Material 树组件之间绘制连接线。我有一个问题,因为当我展开树节点时,路径会向上或向下移动,但我希望它保持在同一位置。当我折叠树节点时,我想将路径从子节点移动到父节点。

我尝试过转换:翻译或矩阵,但我不知道如何计算值。

我应该使用变换还是编辑路径的“d”属性??

这就是我创建 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;
}

这是我绘制连接路径的方式:

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);
}

我如何连接元素:

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]);
}

以及我如何在树展开时尝试转换路径:

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;
}

我所拥有的图片:

How my tree with path look like

What happen when I expand tree

【问题讨论】:

    标签: javascript typescript svg


    【解决方案1】:

    您将viewBox 属性与preserveAspectRatio="none" 组合在一起。这样做的效果是,<svg> 元素始终设置为填充包含元素的高度,如果容器的大小发生变化,它会不断重新缩放 SVG 内所有坐标的含义。

    当建议通常是添加一个 viewBox 时,在您的特殊情况下,省略这两个属性实际上会有所帮助。 <svg>元素本身的坐标系(“初始视口”)及其内容的坐标系(“初始用户坐标系”)是两个different entities

    每个 SVG 视口都会生成一个视口坐标系和一个用户坐标系,最初是相同的。在视口的元素上提供viewBox 会相对于视口坐标系转换用户坐标系...

    只有在没有viewBox 属性的情况下,即使<svg> 元素改变其大小,这两个坐标系也会重合。

    当您的坐标与 SVG 外部的内容相关时,例如您的 a, b 参数,它们只有在树大小发生变化时才有机会保持有意义,前提是两者之间没有坐标转换。

    您的代码手动进行了一些坐标转换,以从屏幕视口坐标到假定的 100 * 100 视图框。删除所有这些。您需要的是连接项目与<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]);
    }
    

    【讨论】:

    • 好的,所以当我展开树时,svg 容器的大小会改变,这就是我的线条改变位置的原因。但是没有 viewBox svg 在窗口调整大小时不能正确缩放。 “a”和“b”参数保存连接的树元素位置的值。
    • 你知道如何在没有 viewBox 属性的情况下正确缩放 svg,这样当树大小改变时我的线条不会移动?
    • 重点不是缩放svg content,而只是&lt;svg&gt; 元素本身,通过设置height:100%; width:100%来实现。
    • 每次位置发生变化时,按照drawConnector 方法重新计算路径并更改属性。
    • 但是当我没有 viewBox 时,路径不会绘制在应有的位置,而是绘制在左上角。
    猜你喜欢
    • 2020-05-15
    • 2019-04-14
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 2017-08-23
    • 1970-01-01
    • 2020-11-02
    相关资源
    最近更新 更多