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