Ответ 1
Другие здесь уже упомянули SVGLocatable.getBBox()
, что полезно для захвата ограничивающей рамки элемента через собственную локальную систему координат. К сожалению, как вы заметили, это не учитывает никаких преобразований, выполняемых над элементом или его родительскими элементами.
Есть несколько других доступных функций, которые помогут вам получить тонну при работе с этими преобразованиями.
SVGLocatable.getScreenCTM()
дает вам SVGMatrix
, представляющий преобразования, необходимые для преобразования из координат окна просмотра в локальные координаты вашего элемента. Это отлично, потому что он будет принимать во внимание преобразования, применяемые к вызываемому ему элементу, и любые преобразования, применяемые к родительским элементам. К сожалению, он также учитывает, где именно элемент находится на экране, а это означает, что если у вас есть контент до вашего документа svg или даже только некоторые поля вокруг него, возвращаемая матрица будет включать это пространство в качестве перевода.
Element.getBoundingClientRect()
позволит вам учитывать это пространство. Если вы вызываете эту функцию на самом документе SVG, вы можете узнать, насколько SVG смещен на экране.
Тогда все, что вам нужно сделать, это объединить два, когда вы хотите конвертировать между системами координат. ЗДЕСЬ - это хорошая информация о том, как работает 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
, чтобы вы могли пройти в целевом элементе, но, надеюсь, это приведет вас в правильном направлении. Удачи!
Лучшей версией будет 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);