Получение позиций экрана узлов D3 после преобразования

Я пытаюсь получить положение экрана на узле после преобразования макета с помощью d3.behavior.zoom(), но мне не очень везет. Как я могу получить фактическую позицию узла в окне после перевода и масштабирования макета?

mouseOver = function(node) {
  screenX = magic(node.x); // Need a magic function to transform node
  screenY = magic(node.y); // positions into screen coordinates.
};

Любое руководство будет оценено.

РЕДАКТИРОВАТЬ: "узел" выше является узлом силового макета, поэтому его свойства x и y устанавливаются симуляцией и остаются постоянными после того, как симуляция останавливается, независимо от того, какой тип преобразования применяется.

РЕДАКТИРОВАТЬ: Стратегия, которую я использую для преобразования SVG, основана на поведении d3, которое описано здесь: SVG Geometric Zooming.

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
  .append("g");

svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height);

svg.selectAll("circle")
    .data(data)
  .enter().append("circle")
    .attr("r", 2.5)
    .attr("transform", function(d) { return "translate(" + d + ")"; });

function zoom() {
  svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}

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

РЕДАКТИРОВАТЬ: я работаю над этой проблемой, используя координаты мыши вместо координат узла, так как меня интересует положение узла, когда узел находится над курсором мыши. Это не совсем то поведение, за которым я следую, но оно работает по большей части и лучше, чем ничего.

РЕДАКТИРОВАТЬ: Решением было получить текущую матрицу преобразования элемента svg с помощью element.getCTM(), а затем использовать ее для смещения координат x и y в относительное к экрану состояние. Увидеть ниже.

2 ответа

Решение

Ты можешь попробовать node.getBBox() чтобы получить положение пикселей жесткой ограничительной рамки вокруг формы узла после любого transform был применен. Смотрите здесь для получения дополнительной информации: ссылка.

РЕДАКТИРОВАТЬ:

getBBox не работает так, как я думал. Поскольку прямоугольник определяется в терминах преобразованного координатного пространства, он всегда относительно родительского <g> и поэтому всегда будет одинаковым для содержащихся форм.

Существует еще одна функция element.getBoundingClientRect, которая, по-видимому, достаточно широко поддерживается и возвращает свой прямоугольник в пиксельной позиции относительно верхнего левого угла порта просмотра браузера. Это может приблизить вас к тому, что вы хотите, без необходимости напрямую связываться с матрицей преобразования.

Похоже, решение моего первоначального вопроса выглядит примерно так:

(Обновлено для поддержки преобразований вращения.)

// The magic function.
function getScreenCoords(x, y, ctm) {
    var xn = ctm.e + x*ctm.a + y*ctm.c;
    var yn = ctm.f + x*ctm.b + y*ctm.d;
    return { x: xn, y: yn };
}

var circle = document.getElementById('svgCircle'),
cx = +circle.getAttribute('cx'),
cy = +circle.getAttribute('cy'),
ctm = circle.getCTM(),
coords = getScreenCoords(cx, cy, ctm);
console.log(coords.x, coords.y); // shows coords relative to my svg container

Кроме того, это также можно сделать с помощью свойств translate и scale из d3.event (если преобразования вращения не нужны):

// This function is called by d3's zoom event.
function zoom() {

// The magic function - converts node positions into positions on screen.    
function getScreenCoords(x, y, translate, scale) {
    var xn = translate[0] + x*scale;
    var yn = translate[1] + y*scale;
    return { x: xn, y: yn };
}

// Get element coordinates and transform them to screen coordinates.
var circle = document.getElementById('svgCircle');
cx = +circle.getAttribute('cx'),
cy = +circle.getAttribute('cy'),
coords = getScreenCoords(cx, cy, d3.event.translate, d3.event.scale);
console.log(coords.x, coords.y); // shows coords relative to my svg container

  // ...
}

РЕДАКТИРОВАТЬ: я нашел приведенную ниже форму функции, чтобы быть наиболее полезным и общим, и, кажется, стоит где getBoundingClientRect падает. Более конкретно, когда я пытался получить точные положения узлов SVG в проекте макета силы D3, getBoundingClientRect дал неточные результаты, в то время как приведенный ниже метод вернул circle точные координаты элемента в нескольких браузерах.

(Обновлено для поддержки преобразований вращения.)

// Pass in the element and its pre-transform coords
function getElementCoords(element, coords) {
    var ctm = element.getCTM(),
    x = ctm.e + coords.x*ctm.a + coords.y*ctm.c,
    y = ctm.f + coords.x*ctm.b + coords.y*ctm.d;
    return {x: x, y: y};
};

// Get post-transform coords from the element.
var circle = document.getElementById('svgCircle'),
x = +circle.getAttribute('cx'),
y = +circle.getAttribute('cy'),
coords = getElementCoords(circle, {x:x, y:y});

// Get post-transform coords using a 'node' object.
// Any object with x,y properties will do.
var node = ..., // some D3 node or object with x,y properties.
circle = document.getElementById('svgCircle'),
coords = getElementCoords(circle, node);

Функция работает, получая матрицу преобразования элемента DOM, а затем используя информацию поворота, масштабирования и преобразования матрицы, чтобы вернуть пост-преобразованные координаты данного объекта узла.

Другие вопросы по тегам