javascript - 如何将鼠标移动距离转换为 SVG 坐标空间?
问题描述
我在这里对 HSL 空间中 CSS4 颜色关键字的分布进行了 SVG 可视化:https ://meyerweb.com/eric/css/colors/hsl-dist.html
我最近通过鼠标滚轮添加了缩放,并通过鼠标咔嗒声和拖动进行平移。我可以使用 , 将点从屏幕空间转换为 SVG 坐标空间matrixTransform
,.getScreenCTM()
并且.inverse()
感谢我在网上找到的示例代码,但是如何在拖动过程中转换鼠标移动?现在我只是viewBox
通过 X 和 Y 值来移动坐标event
,这意味着放大时图像拖动比鼠标移动更快。
例如,假设我放大了图像并拖动以平移,我将鼠标向左并略微向下猛拉。 event.movementX
返回-37
和event.movementY
返回6
。如何确定 SVG 坐标中的距离,以便viewBox
坐标正确移动?
(注意:我知道有这类东西的库,但我故意编写普通的 JS 代码以了解更多关于 SVG 和 JS 的信息。所以请不要发布“大声笑只使用库 X ”然后就这样吧。谢谢!)
编辑添加:我被要求发布代码。发布整个 JS 似乎过长,但这是触发mousemove
事件的函数:
function dragger(event) {
var target = document.getElementById('color-wheel');
var coords = parseViewBox(target);
coords.x -= event.movementX;
coords.y -= event.movementY;
changeViewBox(target,coords);
}
如果需要更多,请在链接页面上查看源代码;所有的 JS 都在页面的顶部。除了只包含可视化的所有 HSL 值和颜色名称的文件之外,没有什么是外部的。
解决方案
我的建议:不要担心事件的movementX
/Y
属性。只需担心鼠标从哪里开始以及现在在哪里。
(这还有一个额外的好处,即即使您错过了一些事件,您也可以获得相同的结果:可能是因为鼠标移出窗口,或者可能是因为您想对事件进行分组,因此每个动画帧只运行一次代码。)
对于鼠标开始的位置,您可以在 mousedown 事件上进行测量。使用您使用的方法,使用.getScreenCTM().inverse()
和将其转换为 SVG 坐标中的位置.matrixTransform()
。在此转换之后,您不必关心屏幕上的这个点在哪里。您只关心它在图片中的位置。这就是图片中的一点,您总是要移动到鼠标下方。
在 mousemove 事件上,您使用相同的转换方法来找出鼠标当前在当前 SVG 坐标系中的位置。然后,您可以计算出距离您想要在鼠标下方的点(同样,在 SVG 坐标中)有多远。这是您用来转换图形的数量。我已经按照您的示例进行了转换,并通过移动x
和的y
部分来进行转换viewBox
:
function move(e) {
var targetPoint = svgCoords(event, svg);
shiftViewBox(anchorPoint.x - targetPoint.x,
anchorPoint.y - targetPoint.y);
}
您还可以在 SVG 内的transform
组(元素)上使用 a 来移动图形;只需确保对从/事件坐标转换的调用<g>
使用相同的组元素。getScreenCTM()
clientX
Y
拖动平移的完整演示。我跳过了你所有的绘图代码和缩放效果。但是缩放应该仍然有效,因为您保存在全局值中的唯一位置已经转换为 SVG 坐标。
var svg = document.querySelector("svg");
var anchorPoint;
function shiftViewBox(deltaX, deltaY) {
svg.viewBox.baseVal.x += deltaX;
svg.viewBox.baseVal.y += deltaY;
}
function svgCoords(event,elem) {
var ctm = elem.getScreenCTM();
var pt = svg.createSVGPoint();
// Note: rest of method could work with another element,
// if you don't want to listen to drags on the entire svg.
// But createSVGPoint only exists on <svg> elements.
pt.x = event.clientX;
pt.y = event.clientY;
return pt.matrixTransform(ctm.inverse());
}
svg.addEventListener("mousedown", function(e) {
anchorPoint = svgCoords(event, svg);
window.addEventListener("mousemove", move);
window.addEventListener("mouseup", cancelMove);
});
function cancelMove(e) {
window.removeEventListener("mousemove", move);
window.removeEventListener("mouseup", cancelMove);
anchorPoint = undefined;
}
function move(e) {
var targetPoint = svgCoords(event, svg);
shiftViewBox(anchorPoint.x - targetPoint.x,
anchorPoint.y - targetPoint.y);
}
body {
display: grid;
margin: 0;
min-height: 100vh;
}
svg {
margin: auto;
width: 70vmin;
height: 70vmin;
border: thin solid gray;
cursor: move;
}
<svg viewBox="-40 -40 80 80">
<polygon fill="skyBlue"
points="0 -40, 40 0, 0 40 -40 0" />
</svg>
推荐阅读
- javascript - 如何正确地将数组传递给 JSON
- javascript - 循环遍历包含布尔值的对象
- ssh - CLion WSL ssh 会话与 x11
- ruby-on-rails - Rails 无法执行 db:migrate,数据库被锁定并且没有模式迁移表
- ruby-on-rails - Rails hide Model field by default to every query
- css - Rem 调整大小在 Safari 中不起作用
- single-sign-on - Keycloak - 限制每个客户端/应用程序的用户访问
- javascript - 在 Firebase Cloud Functions 中使用 async/await 时返回一个 Promise
- unity3d - Unity使用Raycast单击其他对象后面的对象
- android - 如何在 MVP 模式中实现 Cursor Loader?