【问题标题】:Getting Screen Positions of D3 Nodes After Transform获取变换后 D3 节点的屏幕位置
【发布时间】:2013-09-04 10:35:50
【问题描述】:

在布局被 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 属性将变换应用于容器元素。

编辑:我正在通过使用鼠标坐标而不是节点坐标来解决这个问题,因为当节点用鼠标指针悬停在节点上时,我对节点位置感兴趣。这不完全是我所追求的行为,但它在大多数情况下都有效,而且聊胜于无。

编辑:解决方案是使用 element.getCTM() 获取 svg 元素的当前转换矩阵,然后使用它将 x 和 y 坐标偏移到屏幕相对状态。见下文。

【问题讨论】:

  • 我怀疑答案可能涉及使用 SVG 元素 Basic Data Types 的 getCTM() 和/或 getScreenCTM() 做一些事情。我想知道是否有辅助方法可以对这些函数返回的矩阵进行操作,例如 Inverse(tm) 或类似的东西。

标签: d3.js position zooming transform


【解决方案1】:

我原来的问题的解决方案看起来像这样:

(已更新以支持旋转变换。)

// 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 中的 translate 和 scale 属性来完成(如果不需要旋转变换):

// 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 倒下的地方站起来。更具体地说,当我试图在 D3 强制布局项目中获取准确的 SVG 节点位置时,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 元素的变换矩阵,然后使用矩阵旋转、缩放和平移信息返回给定节点对象的变换后坐标。

【讨论】:

  • 非常感谢这让我很开心,我在互联网上搜索了几个小时以寻找解决方案。
  • 嗯,你能确认一下,这个功能在应用旋转时仍然不起作用吗?因为你没有使用 ctm.b 和 ctm.c 来转换坐标。
  • 有人知道如何将旋转变换应用到 getElementCoords() 函数吗?
  • @poliu2s,看起来我在之前的评论中反转了旋转元素。这应该可以工作:x = ctm.e + coords.x*ctm.a + coords.y*ctm.cy = ctm.f + coords.x*ctm.b + coords.y*ctm.d
  • 感谢您添加旋转变换的代码。工作。
【解决方案2】:

您可以尝试node.getBBox() 在应用任何transform 后获取节点形状周围紧密边界框的像素位置。更多信息请看这里:link

编辑:

getBBox 不像我想象的那样工作。由于矩形是根据转换后的坐标空间定义的,因此它始终相对于父 <g>,因此对于包含的形状将始终相同。

还有另一个名为element.getBoundingClientRect 的函数似乎得到了广泛的支持,它返回相对于浏览器视口左上角的像素位置的矩形。这可能会让你更接近你想要的,而无需直接弄乱变换矩阵。

【讨论】:

  • 感谢您的链接。文档肯定说 getBBox() 是转换后的,但我得到的 node.x 和 node.y 的坐标与我对 el.getBBox().x 和 el.getBBox().y 的坐标相同。请注意,“节点”是一个强制布局节点,它是选择数据的一部分。
  • 在调用 getBBox 时应用于元素的变换是什么?
  • 我编辑了我的帖子,试图回答你的问题。我正在通过容器的 transform 属性应用平移和缩放变换。 SVG Geometric Zooming
  • 如果您查看 Chrome 开发工具的 Elements 选项卡,元素上的 transform 属性的实际值是多少? getBBox 告诉你的是否有意义?
  • 转换实际上是应用于父 'svg:g' 元素。然而,检查 svg 节点的 cx 和 cy 表明这些值在布局转换时不会改变,并且这些值与 getBBox() 返回的值一致。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多