javascript - 缩放后在 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,如果这有所不同的话。
当然必须有一种简单的方法来进行转换?
解决方案
为了将 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 引用相同的东西,在本例中是矩形。
推荐阅读
- swift - 组合惰性序列变换的 Swift 问题
- typescript - 我可以使用索引签名为枚举创建一组描述吗?
- backtracking - 基于冲突的回跳中的冲突集排序
- hibernate - 级联获取实体Jackson/JPA的限制大小
- php - 如何使用 Symfony 的 CLI 命令在 TYPO3 v9 中创建文件关系?
- python - 训练 LSTM 模型
- c# - 如何使用 2 个数据表创建多行标题并将其与 GridView 中的其他列合并
- php - PHPCS - 实例化时强制类名大小写
- c++ - Golang CGO 异常 0x40010006
- ruby-on-rails - 活动管理员:添加管理员用户在本地工作但不在生产中