【问题标题】:How to get position of empty SVG group?如何获取空 SVG 组的位置?
【发布时间】:2016-03-21 11:05:05
【问题描述】:

我正在使用 d3,但我在定位和空组方面遇到了麻烦。

我有这个 svg:

<svg id="mysvg" height="200" width="1350" xmlns="http://www.w3.org/2000/svg">
  <g transform="translate(50, 6)">
    <g class="a">
      <g transform="translate(0,161.3919677734375)" style="opacity: 1;">
        <line y2="0" x2="-6"></line>
      </g>
    </g>
    <g class="b">
    </g>
  </g>
</svg>

我想动态添加一条线b类分组,我想添加它以便线的第一个点的坐标重合与a组内线的第二个点的坐标。

由于 SVG 对象的坐标是相对于它们的容器的,为了获得相对坐标,我首先使用 getBoundingClientRect() 获得 g.a 内线的绝对位置和 g.b 的绝对位置

问题是g.b 的坐标,如果为空,则完全混乱。我必须创建一个虚假对象才能正确获取它们:

d3.select("#mysvg .b").append("circle").attr("r", 0).attr("fill", "transparent")
                                       .attr("cx", 0).attr("cy", 0);

此外,如果我创建半径大于零的圆,则其组b的位置将移动

【问题讨论】:

  • Empty &lt;g&gt; 对象并没有真正的位置,因为它们会根据内容自动调整大小。如果你把东西放进去,&lt;g&gt; 的位置应该会改变,这就是&lt;g&gt; 元素的工作原理。
  • getBBox() 帮助我获得了 svg 元素的位置。也许你不知道这就是我提到它的原因。

标签: javascript d3.js svg


【解决方案1】:

您可以使用基本的 DOM 接口在两个坐标系之间进行转换。接口InterfaceSVGLocatable 定义了方法getTransformToElement() 其中

返回从当前元素上的用户坐标系(应用'transform'属性,如果有的话)到参数元素上的用户坐标系(应用其'transform'属性,如果有的话)的变换矩阵.

值得注意的是,Chrome 48+ (Issue 524432) 已取消对此的支持。但是,有一个相当苗条的 polyfill 可用:

// Polyfill needed for Chrome 48+
SVGElement.prototype.getTransformToElement = 
    SVGElement.prototype.getTransformToElement || function(elem) {
        return elem.getScreenCTM().inverse().multiply(this.getScreenCTM());
    };

因为这只是一行简单的代码,直接在代码中使用它可能会更容易。

您可以使用帮助器 SVGPoint 来获取 transform 从组中的元素 a 到组中的元素 b 所需的转换矩阵:

// Create a helper point
var point = document.getElementById("mysvg").createSVGPoint();
point.x = line.getAttribute("x2");
point.y = line.getAttribute("y2");

// Calculate starting point of new line based on transformation between coordinate systems.
point = point.matrixTransform(line.getTransformToElement(groupB));

看看这个工作示例,它在b 组中绘制一条红线,从a 组中的行末尾开始到坐标 (100, 100):

// Polyfill needed for Chrome >48
SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) {
    return elem.getScreenCTM().inverse().multiply(this.getScreenCTM());
};

var groupB = document.querySelector("g.b");
var line = document.querySelector(".a line");

// Create a helper point
var point = document.getElementById("mysvg").createSVGPoint();
point.x = line.getAttribute("x2");
point.y = line.getAttribute("y2");

// Calculate starting point of new line based on transformation between coordinate systems.
point = point.matrixTransform(line.getTransformToElement(groupB));

// D3 applied to simplify matters a little.
// New line is drawn from end of line in group a to (100,100).
d3.select(groupB).append("line")
    .attr({
        "x1": point.x,
        "y1": point.y,
        "x2": 100,
        "y2": 100
    });
line {
  stroke: black;
}

.b line{
  stroke:red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="mysvg" height="200" width="1350" xmlns="http://www.w3.org/2000/svg">
  <g transform="translate(50, 6)">
    <g class="a">
      <g transform="translate(0,161.3919677734375)" style="opacity: 1;">
        <line y2="0" x2="-40"></line>
      </g>
    </g>
    <g class="b">
    </g>
  </g>
</svg>

由于与问题本身提出的方法相比,cmets 提出了有关这种方法的性能的问题,因此我设置了一个小jsPerf test case 来比较两者。我怀疑这个答案的代码明显优于原始代码,因为操作 DOM 始终是一项昂贵的操作。另一方面,矩阵计算只需使用来自 DOM 的值,无需进行任何修改。结果清楚地支持了这一假设,在 FF、IE 和 Chrome 中插入圆圈至少慢 95%。

【讨论】:

  • Firefox 似乎没有getScreenCTM()。我使用的是 Firefox 41.0.2。
  • 我认为它应该在那里。在较低版本中似乎有很多错误,但我不认为它已经消失了。
  • @MarcoSulla FF 实际上有 getTransformToElement () 方法,甚至不应该运行 polyfill 的代码。您是按原样使用 polyfill 还是提取了单层?在执行后者时,请记住将 this 替换为要转换到的元素,因为在这种情况下,this 很可能会引用另一个对象,而该对象可能确实没有实现该方法。
  • @MarcoSulla Firefox 确实有 getScreenCTM
  • 是的,抱歉,getScreenCTM 受支持并且代码有效,但就我所见,我总是需要一个帮助点。这个解决方案更快吗?
猜你喜欢
  • 2014-08-29
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
  • 1970-01-01
  • 2014-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多