Подтвердить что ты не робот

Как получить абсолютные координаты объекта внутри группы <g>?

Это может быть FAQ, поэтому не стесняйтесь указать мне на другой ответ. Тема трудно найти.

Если я хочу использовать 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 замечает в комментарии к этому вопросу, что наследуемое не является парой координат, а атрибутом transform. Поэтому я могу выбрать <g> и получить значение атрибута transform:

console.log(d3.select("#mygroup").attr("transform"));

который печатает, например:

"Поворот (-125,93) перевод (0, -25)"

Должен ли я разобрать это, чтобы узнать, где находится <g> в абсолютной системе координат?

4b9b3361

Ответ 1

Другие здесь уже упомянули SVGLocatable.getBBox(), что полезно для захвата ограничивающей рамки элемента через собственную локальную систему координат. К сожалению, как вы заметили, это не учитывает никаких преобразований, выполняемых над элементом или его родительскими элементами.

Есть несколько других доступных функций, которые помогут вам получить тонну при работе с этими преобразованиями.

SVGLocatable.getScreenCTM() дает вам SVGMatrix, представляющий преобразования, необходимые для преобразования из координат окна просмотра в локальные координаты вашего элемента. Это отлично, потому что он будет принимать во внимание преобразования, применяемые к вызываемому ему элементу, и любые преобразования, применяемые к родительским элементам. К сожалению, он также учитывает, где именно элемент находится на экране, а это означает, что если у вас есть контент до вашего документа svg или даже только некоторые поля вокруг него, возвращаемая матрица будет включать это пространство в качестве перевода.

Element.getBoundingClientRect() позволит вам учитывать это пространство. Если вы вызываете эту функцию на самом документе SVG, вы можете узнать, насколько SVG смещен на экране.

Тогда все, что вам нужно сделать, это объединить два, когда вы хотите конвертировать между системами координат. ЗДЕСЬ - это хорошая информация о том, как работает SVGMatrix. На данный момент важно знать, что SVGMatrix - объект с шестью свойствами a, b, c, d, e и f, которые представляют собой преобразование следующим образом:

svg matrix equations

Допустим, у вас есть переменная 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, чтобы вы могли пройти в целевом элементе, но, надеюсь, это приведет вас в правильном направлении. Удачи!

Лучшей версией будет factory, который генерирует функцию преобразования для любого заданного контекста документа и 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);

Ответ 2

D3 имеет встроенную функцию для синтаксического анализа svg-преобразований: d3.transform

Вы можете использовать его для получения массива translate ([x, y]) преобразования, то есть:

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

Ответ 3

Чтобы получить границы элемента SVG, у вас есть два варианта:

  • getBBox(), который работает на всех (графических) элементах SVG. Он получает ограничивающий прямоугольник элемента в локальном координатном пространстве. Если элемент имеет атрибут transform, это повлияет на bbox, но если родительский элемент имеет transform, он не будет отображаться в возвращенном bbox.

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

  • getBoundingClientRect(), который является функцией HTML-элемента, но также работает для элементов SVG. Он возвращает границы элемента в пространстве экрана (после применения всех преобразований).

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

Ответ 4

@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 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);
}

См. также: Прямоугольные координаты после преобразования

Чтобы найти края вашего круга, например:

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);

По общему признанию, это не так интересно с простым кругом, но после того, как окружность была сдвинута или растянута, это отличный инструмент для получения координат.

W3 Spec

Microsoft более понятный учебник