这可能是常见问题,所以请随时向我指出另一个答案。该主题很难搜索。
如果我想使用d3.js获取在SVG对象中显式声明的属性,或者我已经使用D3明确地放置了该属性,我可以使用d3.select
轻松获取属性的值。例如,这打印300:
...
<circle id="mycircle" r="10" cx="100" cy="200">
...
d3.select("#mycircle").attr("cx", 300);
console.log(d3.select("#mycircle").attr("cx"));
如果我没有明确设置属性的值,但是从<g>
组隐式“设置”怎么办?或者:我如何使用代码找出<g>
组的中心位置?我想要一些方法来确定<svg>
对象的绝对坐标系中<g>
内的事物的位置。如果我知道<g>
在哪里,它在太空中的定位等等,我可以弄清楚里面的点是什么。我怎么能这样做?
BigBadaboom在对this question的答案的评论中发表评论说,继承的不是一对坐标,而是一个transform
属性。所以我可以选择<g>
并获取transform属性的值:
console.log(d3.select("#mygroup").attr("transform"));
打印,例如:
“旋转(-125.93)翻译(0,-25)”
我是否必须解析它以找出<g>
在绝对坐标系中的位置?
答案 0 :(得分:35)
其他人已经提到SVGLocatable.getBBox()
,它对于根据自己的局部坐标系统抓取元素的边界框非常有用。不幸的是,正如您所注意到的,这并未考虑对元素或其父元素所做的任何转换。
还有一些其他功能可以帮助您在处理这些变换时提供帮助。
SVGLocatable.getScreenCTM()
为您提供SVGMatrix
,表示从视口坐标转换为元素的本地坐标所需的转换。这很好,因为它将考虑应用于调用它的元素的变换,以及应用于父元素的任何变换。不幸的是,它还考虑了屏幕上元素的确切位置,这意味着如果您在svg文档之前有内容,或者甚至只是它周围的一些边距,则返回的矩阵将包含该空间作为翻译。
Element.getBoundingClientRect()
将允许您考虑该空间。如果在SVG文档本身上调用此函数,则可以找出SVG在屏幕上的偏移量。
然后,当您想要在坐标系之间进行转换时,您所要做的就是将两者结合起来。 HERE是关于SVGMatrix
如何运作的一些很好的信息。现在要知道的重要事项是SVGMatrix
是一个包含六个属性a
,b
,c
,d
,e
的对象和f
代表转换如下:
假设您有一个变量svgDoc
,它是对svg文档的引用(不是d3选择,而是元素本身)。然后,您可以创建一个函数,该函数将转换为svg元素elem
的坐标系,如下所示。
function convertCoords(x,y) {
var offset = svgDoc.getBoundingClientRect();
var matrix = elem.getScreenCTM();
return {
x: (matrix.a * x) + (matrix.c * y) + matrix.e - offset.left,
y: (matrix.b * x) + (matrix.d * y) + matrix.f - offset.top
};
}
然后,假设你想在elem
的中间放一个点,你可以这样做:
var bbox = elem.getBBox(),
middleX = bbox.x + (bbox.width / 2),
middleY = bbox.y + (bbox.height / 2);
var absoluteCoords = convertCoords(middleX, middleY);
var dot = svg.append('circle')
.attr('cx', absoluteCoords.x)
.attr('cy', absoluteCoords.y)
.attr('r', 5);
当然,你可能想要概括 convertCoords
函数,这样你就可以传入目标元素,但希望这会让你朝着正确的方向前进。祝你好运!
更好的实现是为任何给定元素和svg文档上下文生成转换函数的工厂:
function makeAbsoluteContext(element, svgDocument) {
return function(x,y) {
var offset = svgDocument.getBoundingClientRect();
var matrix = element.getScreenCTM();
return {
x: (matrix.a * x) + (matrix.c * y) + matrix.e - offset.left,
y: (matrix.b * x) + (matrix.d * y) + matrix.f - offset.top
};
};
}
这可以如下使用elem
和svgDoc
作为天真的例子:
var bbox = elem.getBBox(),
middleX = bbox.x + (bbox.width / 2),
middleY = bbox.y + (bbox.height / 2);
// generate a conversion function
var convert = makeAbsoluteContext(elem, svgDoc);
// use it to calculate the absolute center of the element
var absoluteCenter = convert(middleX, middleY);
var dot = svg.append('circle')
.attr('cx', absoluteCenter.x)
.attr('cy', absoluteCenter.y)
.attr('r', 5);
答案 1 :(得分:7)
D3有一个内置函数来解析svg变换:d3.transform
您可以使用它来获取转换的转换数组([x,y]),即:
var transformText = d3.select("#mygroup").attr("transform");
var translate = d3.transform(transformText).translate; //returns [0,-25]
答案 2 :(得分:6)
要获取SVG元素的边界,您有两个选择:
getBBox()
适用于所有(图形)SVG元素。它获取局部坐标空间中元素的边界框。如果元素具有transform
属性,则会影响bbox,但如果父元素具有transform
,则它不会反映在返回的bbox中。
getBoundingClientRect()
这是一个HTML元素函数,但也适用于SVG元素。它返回屏幕空间中元素的边界(在应用了所有变换之后)。
https://developer.mozilla.org/en-US/docs/Web/API/Element.getBoundingClientRect
答案 3 :(得分:5)
@Jshanley的优秀答案实际上很容易在使用SVGPoint矩阵转换的原始JavaScript(或任何框架)中实现。
/**
* Get a new XY point in SVG-Space, where X and Y are relative to an existing element. Useful for drawing lines between elements, for example
* X : the new X with relation to element, 5 would be '5' to the right of element's left boundary. element.width would be the right edge.
* Y : the new Y coordinate, same principle applies
* svg: the parent SVG DOM element
* element: the SVG element which we are using as a base point.
*/
function getRelativeXY(x, y, svg, element){
var p = svg.createSVGPoint();
var ctm = element.getCTM();
p.x = x;
p.y = y;
return p.matrixTransform(ctm);
}
另见: Rectangle coordinates after transform
为了找到圆圈的边缘,例如:
var leftTangent = getRelativeXY(circle.cx-circle.r, circle.y, svg, circle);
var rightTangent = getRelativeXY(circle.cx+circle.r, circle.y, svg, circle);
var topTangent= getRelativeXY(circle.cx, circle.y-circle.r, svg, circle);
var bottomTangent= getRelativeXY(circle.cx, circle.y+ circle.r, svg, circle);
var deadCenter= getRelativeXY(circle.cx, circle.y, svg, circle);
不可否认,对于一个普通的圆圈来说并不是那么有趣,但是一旦圆圈被移动或拉伸,它就是获得坐标的好工具。
答案 4 :(得分:2)
这里的好演示:https://codepen.io/netsi1964/pen/pWjwgP
point = point.matrixTransform(svg.getScreenCTM().inverse())