如何获得空SVG组的位置?

时间:2016-03-21 11:05:06

标签: javascript d3.js svg

我正在使用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对象的坐标是相对于它们的容器,为了得到相对坐标,我首先得到g.a内的线的绝对位置和g.b的绝对位置,使用{{1} }

问题是getBoundingClientRect()的坐标,如果它是空的,则完全混乱。我必须创建一个虚假对象才能正确使用它们:

g.b

此外,如果我创建半径大于零的圆圈,其组d3.select("#mysvg .b").append("circle").attr("r", 0).attr("fill", "transparent") .attr("cx", 0).attr("cy", 0); 的位置将会改变

1 个答案:

答案 0 :(得分:3)

您可以使用基本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从组a中的元素到组b中的元素获取transform所需的转换矩阵:

// 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):

&#13;
&#13;
// 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
    });
&#13;
line {
  stroke: black;
}

.b line{
  stroke:red;
}
&#13;
<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>
&#13;
&#13;
&#13;

由于问题在关于这种方法的表现的评论中提出,与问题本身提出的问题相比,我设置了一点jsPerf test case来比较两者。我怀疑这个答案的代码明显优于原来的代码,因为操纵DOM总是一项昂贵的操作。另一方面,矩阵计算只需使用DOM中的值而不应用任何修改。结果清楚地反映了这个假设,在FF,IE和Chrome中插入圆圈的速度至少慢了95%。

enter image description here