首页 > 解决方案 > 缩放后在 SVG 坐标中获取鼠标位置

问题描述

使用 D3 v4、SVG 和缩放行为,在 mousemove 上,我想在 SVG 的坐标系中显示鼠标坐标。

mousemove 事件似乎只提供客户端/屏幕坐标。

如何将这些坐标转换为反映当前缩放/平移/等变换的 SVG 坐标?

我可以看到使用轴/刻度/等的示例,但我没有创建图表,也没有使用轴/等——我使用 D3.js 制作交互式图表。

我已经尝试过 crateSVGPoint/getScreenCTM 方法,但是 (a) 并不真正理解它,因此不确定如何将它应用到我的代码中,并且 (b) 当我缩放/平移时它似乎不起作用 - 我只是获取客户端/屏幕坐标。EG:这是我的 mousemove 事件中转换为 SVG 坐标的代码:

var theSvg = document.getElementById('svgItem');
var pt = theSvg.createSVGPoint();
var cursorPoint = function(evt){
  pt.x = evt.clientX; pt.y = evt.clientY;
  return pt.matrixTransform(theSvg.getScreenCTM().inverse());
}
var loc = cursorPoint(d3.event);

//the pair of co-ordinates should be different on zoom, but aren't
d3.select(".statusBarText").text("move (" + d3.event.x + "," + d3.event.y + ") (" + loc.x + "," + loc.y + ")");

值得一提的是,缩放行为适用于 SVG 元素的 SVG 组元素。缩放效果很好;我可以看到应用于组的翻译/缩放。

我试图修改上面的内容以在已转换的组元素上调用 createSVGPoint() 但得到关于 createSVGPoint() 不是函数的错误;我猜这仅适用于 DOM 中的 SVG 元素...

我正在使用的 SVG 元素上还设置了一个 viewBox,如果这有所不同的话。

当然必须有一种简单的方法来进行转换?

标签: javascriptd3.jssvgzooming

解决方案


为了将 d3.mouse 返回的坐标转换为 d3.zoom 使用的坐标系,除了 d3.mouse 返回的坐标外,还需要获取缩放变换。我将在这里强调一种方法。

您可以通过以下方式获取当前缩放变换:

d3.zoomTransform(selection.node());

其中 selection 是调用缩放的 d3 选择。虽然 d3.mouse() 为我们提供了鼠标相对于容器的屏幕坐标,但我们可以使用它和转换为我们提供缩放和平移的坐标transform.invert(),它需要一个点并返回缩放后的坐标:

  var xy = d3.mouse(this);         // relative to specified container
  var transform = d3.zoomTransform(selection.node());
  var xy1 = transform.invert(xy);  // relative to zoom

这是一个快速示例,显示了与鼠标坐标相比,缩放坐标的提取。坐标轴只是为了大致了解缩放坐标系是什么(它们显示缩放坐标),但没有它们,功能保持不变:

var svg = d3.select("body")
  .append("svg")
  .attr("width", 500)
  .attr("height", 300)
  .attr("fill","#eee");
  
var g = svg.append("g")
  .attr("transform","translate(50,50)");
  
var rect = g.append("rect")
  .attr("width",400)
  .attr("height",200)
  .attr("fill","#eee");

var x = d3.scaleLinear().domain([0,400]).range([0,400]);
var y = d3.scaleLinear().domain([0,200]).range([0,200]);

var axisX = d3.axisTop().scale(x).tickSize(-200)
var axisY = d3.axisLeft().scale(y).tickSize(-400);

var gX = g.append("g").call(axisX)
var gY = g.append("g").call(axisY)

var zoom = d3.zoom()
  .scaleExtent([1, 8])
  .on("zoom",zoomed);
  
rect.call(zoom);

rect.on("click", function() {
  var xy = d3.mouse(this);
  
  var transform = d3.zoomTransform(rect.node());
  var xy1 = transform.invert(xy);

  console.log("Mouse:[", xy[0], xy[1], "] Zoomed:[",xy1[0],xy1[1],"]")
})
  
function zoomed() {
  gX.call(axisX.scale(d3.event.transform.rescaleX(x)));
  gY.call(axisY.scale(d3.event.transform.rescaleY(y)));
}
rect {
  cursor: pointer;
}
.tick line {
  stroke: #ccc;
  pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

当然,您必须确保 d3.mouse 和 d3.zoom 引用相同的东西,在本例中是矩形。


推荐阅读