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

17

Я пытаюсь получить положение экрана узла после того, как макет был преобразован 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.
};

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

EDIT: «node» выше - узел компоновки сил, поэтому свойства x и y задаются симуляцией и остаются постоянными после того, как симуляция останавливается, независимо от того, какой тип преобразования применяется.

EDIT: стратегия, которую я использую для преобразования 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.

EDIT: Я работаю над проблемой, используя координаты мыши, а не координаты узлов, так как меня интересует позиция узла, когда узел зависает с указателем мыши. Это не совсем мое поведение, но оно работает по большей части и лучше, чем ничего.

EDIT: решение заключалось в том, чтобы получить текущую матрицу преобразования элемента svg с элементом element.getCTM (), а затем использовать ее для смещения координат x и y в относительное состояние экрана. См. Ниже.

    
задан t.888 01.09.2013 в 02:55
источник
  • Я подозреваю, что ответ может включать в себя что-то делать с getCTM () и / или getScreenCTM () элемента SVG Основные типы данных. Мне интересно, есть ли вспомогательные методы для работы с матрицами, возвращаемыми этими функциями, такими как Inverse (tm) или что-то в этом роде. –  t.888 01.09.2013 в 19:38

2 ответа

7

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

EDIT:

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

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

    
ответ дан Scott Cameron 01.09.2013 в 03:28
источник
  • Спасибо за ссылку. Док определенно говорит, что getBBox () является пост-преобразованием, но я получаю те же координаты для node.x и node.y, как и для el.getBBox (). X и el.getBBox (). Y. Просто отметим, что «узел» - это узел компоновки сил, который является частью данных выбора. –  t.888 01.09.2013 в 04:59
  • Что такое преобразование, которое применяется к элементу в момент вызова getBBox? –  Scott Cameron 01.09.2013 в 08:36
  • Я редактировал сообщение, пытаясь ответить на ваш вопрос. Я применяю трансляцию и масштабирование через атрибут преобразования контейнера. Геометрическое масштабирование SVG –  t.888 01.09.2013 в 18:52
  • Если вы посмотрите на вкладке «Элементы» инструментов Chrome dev, каково фактическое значение атрибута transform для элемента? Имеет ли смысл то, что вам сообщает getBBox? –  Scott Cameron 01.09.2013 в 19:07
  • Преобразование фактически применяется к родительскому элементу 'svg: g'. Однако проверка cx и cy для узла svg показывает, что эти значения не изменяются при преобразовании макета, и эти значения согласуются с значениями, возвращаемыми getBBox (). –  t.888 01.09.2013 в 19:24
Показать остальные комментарии
21

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

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

// 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

В качестве альтернативы это также можно сделать, используя свойства перевода и масштабирования из 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

  // ...
}

EDIT: я нашел, что приведенная ниже форма функции является наиболее полезной и общей, и, похоже, она встает там, где 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, а затем используя поворот матрицы, масштабирование и перевод информации, чтобы вернуть координаты посттрансформации данного объекта узла.

    
ответ дан t.888 01.09.2013 в 21:00
источник
  • Большое спасибо, что это сделало мой день, я много часов искал в Интернете для решения. –  Mr. Sam 21.05.2015 в 09:58
  • Hum, Не могли бы вы подтвердить, что эта функция все еще не работает при правильном применении вращения? Потому что вы не используете ctm.b и ctm.c для преобразования координат. –  Overdrivr 27.11.2015 в 10:30
  • Кто-нибудь знает, как применить преобразования поворота к функции getElementCoords ()? –  poliu2s 26.04.2016 в 07:03
  • @ poliu2s, похоже, что в предыдущем комментарии меняются обратные элементы. Это должно работать: x = ctm.e + coords.x * ctm.a + coords.y * ctm.c и y = ctm.f + coords.x * ctm.b + coords.y * ctm.d. –  t.888 27.04.2016 в 02:03
  • Спасибо за добавление кода для преобразований вращения. Работал. –  akauppi 08.05.2016 в 22:40