首页 > 解决方案 > 如何将鼠标移动距离转换为 SVG 坐标空间?

问题描述

我在这里对 HSL 空间中 CSS4 颜色关键字的分布进行了 SVG 可视化:https ://meyerweb.com/eric/css/colors/hsl-dist.html

我最近通过鼠标滚轮添加了缩放,并通过鼠标咔嗒声和拖动进行平移。我可以使用 , 将点从屏幕空间转换为 SVG 坐标空间matrixTransform.getScreenCTM()并且.inverse()感谢我在网上找到的示例代码,但是如何在拖动过程中转换鼠标移动?现在我只是viewBox通过 X 和 Y 值来移动坐标event,这意味着放大时图像拖动比鼠标移动更快。

例如,假设我放大了图像并拖动以平移,我将鼠标向左并略微向下猛拉。 event.movementX返回-37event.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 值和颜色名称的文件之外,没有什么是外部的。

标签: javascriptsvgmatrix

解决方案


我的建议:不要担心事件的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()clientXY

拖动平移的完整演示。我跳过了你所有的绘图代码和缩放效果。但是缩放应该仍然有效,因为您保存在全局值中的唯一位置已经转换为 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>


推荐阅读