如何在<g>组中获取对象的绝对坐标?

Mar*_*ars 21 svg d3.js

这可能是常见问题,所以请随时向我指出另一个答案.该主题很难搜索.

如果我想使用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"));
Run Code Online (Sandbox Code Playgroud)

如果我没有显式设置属性的值,但它是从<g>组中隐式"设置" 怎么办?或者:我如何使用代码找出<g>组居中的位置?我想要一些方法来确定<svg>对象的绝对坐标系中的内容<g>是什么.如果我知道它在哪里<g>,它是如何在太空中定向的,我可以弄清楚里面的点是什么.我怎样才能做到这一点?

BigBadaboom在评论这个问题的答案时评论说,继承的不是一对坐标,而是一个transform属性.所以我可以选择a <g>并获取transform属性的值:

console.log(d3.select("#mygroup").attr("transform"));
Run Code Online (Sandbox Code Playgroud)

打印,例如:

"旋转(-125.93)翻译(0,-25)"

我是否必须解析它以找出<g>绝对坐标系中的位置?

jsh*_*ley 37

这里的其他人已经提到过SVGLocatable.getBBox(),它可以根据自己的局部坐标系统来抓取元素的边界框.不幸的是,正如您所注意到的,这并未考虑对元素或其父元素所做的任何转换.

还有一些其他功能可以帮助您处理这些变换.

SVGLocatable.getScreenCTM()为您提供SVGMatrix表示从视口坐标转换为元素的本地坐标所需的转换.这很好,因为它将考虑应用于调用它的元素的变换,以及应用于父元素的任何变换.不幸的是,它还考虑了屏幕上元素的确切位置,这意味着如果您在svg文档之前有内容,或者甚至只是它周围的一些边距,则返回的矩阵将包含该空间作为翻译.

Element.getBoundingClientRect()将允许您考虑该空间.如果在SVG文档本身上调用此函数,则可以找出SVG在屏幕上的偏移量.

然后,当您想要在坐标系之间进行转换时,您所要做的就是将两者结合起来.是关于如何SVGMatrix工作的一些很好的信息.要知道,现在最重要的是,一个SVGMatrix是有六个属性的对象a,b,c,d,e,和f它代表一个转型如下:

svg矩阵方程

假设你有一个变量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
  };
}
Run Code Online (Sandbox Code Playgroud)

然后,假设你想在中间放一个点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);
Run Code Online (Sandbox Code Playgroud)

当然,你可能想要概括这个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
    };
  };
}
Run Code Online (Sandbox Code Playgroud)

给出相同elemsvgDoc天真的例子,可以如下使用:

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);
Run Code Online (Sandbox Code Playgroud)

  • 此函数非常适合更新元素相对于另一个动画的位置.但是,当缩小SVG或其坐标系不等于文档的像素时,计算结束.有没有办法考虑缩小的坐标系? (8认同)

Jos*_*osh 8

D3有一个内置函数来解析svg变换: d3.transform

您可以使用它来获取转换的转换数组([x,y]),即:

var transformText = d3.select("#mygroup").attr("transform");
var translate = d3.transform(transformText).translate;  //returns [0,-25]
Run Code Online (Sandbox Code Playgroud)


Pau*_*eau 6

要获得SVG元素的边界,您有两个选择:

  1. getBBox()适用于所有(图形)SVG元素.它获取局部坐标空间中元素的边界框.如果元素有一个transform属性,它将影响bbox,但如果父元素有一个transform,它将不会反映在返回的bbox中.

    http://www.w3.org/TR/SVG/types.html#InterfaceSVGLocatable

  2. getBoundingClientRect()这是一个HTML元素函数,但也适用于SVG元素.它返回屏幕空间中元素的边界(在应用了所有变换之后).

    https://developer.mozilla.org/en-US/docs/Web/API/Element.getBoundingClientRect


Inf*_*ure 6

@Jshanley的优秀答案实际上很容易在原始JavaScript(或任何框架)中使用SVGPoint的矩阵转换实现.

/**
* 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);
}
Run Code Online (Sandbox Code Playgroud)

另请参见: 变换后的矩形坐标

为了找到圆的边缘,例如:

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);
Run Code Online (Sandbox Code Playgroud)

不可否认,对于一个普通的圆圈来说,这并不是那么有趣,但是一旦圆圈被移动或拉伸,它就是获得坐标的好工具.

W3的规格

微软更容易理解教程