缩放后获取鼠标在 SVG 坐标中的位置

Get mouse position in SVG co-ordinates after zoom

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

mousemove 事件似乎只提供 client/screen 坐标。

如何将这些坐标转换为反映当前 zooom/pan/etc 变换的 SVG 坐标?

我可以看到使用 axis/scales/etc 的示例,但我没有创建图表,也没有使用 axis/etc- 我使用 D3.js 作为交互式图表。

我已经尝试过 crateSVGPoint/getScreenCTM 方法,但是 (a) 并不真正理解它,因此我不确定如何将它应用到我的代码中,并且 (b) 它似乎在以下情况下不起作用我 zoom/pan - 我刚得到 client/screen 坐标。 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 组元素。缩放效果很好;我可以看到 translate/scale 正在应用于该组。

我尝试修改上面的代码以在已转换的组元素上调用 createSVGPoint(),但出现有关 createSVGPoint() 不是函数的错误;我想这只适用于 DOM...

中的 SVG 元素

我正在使用的 SVG 元素上还有一个 viewBox 集,如果这有区别的话。

一定有一种简单的方法来进行转换吗?

为了将d3.mouse编辑的坐标return转换为d3.zoom使用的坐标系,除了坐标[=38]之外还需要得到缩放变换=]由 d3.mouse 编辑。我将在这里重点介绍一种方法。

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

d3.zoomTransform(selection.node());

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

  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 引用相同的东西,在本例中是矩形。