【问题标题】:How do I translate mouse movement distances to SVG coordinate space?如何将鼠标移动距离转换为 SVG 坐标空间?
【发布时间】:2019-04-07 23:02:53
【问题描述】:

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

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

例如,假设我放大了图像并拖动以平移,我将鼠标向左并略微向下猛拉。 event.movementX 返回 -37event.movementY 返回 6。如何确定 SVG 坐标中的距离,以便正确移动 viewBox 坐标?

(注意:我知道有一些库可以处理这类事情,但我有意编写香草 JS 代码以了解有关 SVG 和 JS 的更多信息。所以,请不要发布“lol just use library 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 值和颜色名称的文件之外,没有什么是外部的。

【问题讨论】:

  • 嗨,埃里克。这是您可以直接添加到帖子中的代码量吗?
  • 我不确定其中有多少是相关的,但我会添加我认为需要修改的功能。

标签: javascript svg matrix


【解决方案1】:

我的建议: 不要担心事件的movementX/Y 属性。 只需担心鼠标从哪里开始以及现在在哪里。

(这有一个额外的好处,即使你错过了一些事件,你也会得到相同的结果:可能是因为鼠标移出了窗口,或者可能是因为你想对事件进行分组,所以你只在每个动画帧运行一次代码.)

对于鼠标开始的位置,您可以在 mousedown 事件中对其进行测量。 使用您使用的方法将其转换为 SVG 坐标中的位置, 与.getScreenCTM().inverse().matrixTransform()。 在此转换之后,您不必关心 屏幕 这个点在哪里。你只关心它在图片中的位置。这就是图片中的点,您总是要移动到鼠标下方。

在 mousemove 事件中,您使用相同的转换方法来找出鼠标当前在当前 SVG 坐标系中的位置。然后,您可以计算出距离您想要在鼠标下方的点(同样,在 SVG 坐标中)有多远。这就是您用来转换图形的数量。我已经按照您的示例进行了转换,并通过移动viewBoxxy 部分来进行转换:

function move(e) {
  var targetPoint = svgCoords(event, svg);
  shiftViewBox(anchorPoint.x - targetPoint.x,
               anchorPoint.y - targetPoint.y);
}

您还可以在 SVG 内的组(<g> 元素)上使用 transform 移动图形;只需确保对从 clientX/Y 事件坐标转换而来的 getScreenCTM() 调用使用相同的组元素。

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

【讨论】:

    【解决方案2】:

    所以脚本需要一些东西,以便 SVG 移动的矢量与屏幕上鼠标移动的矢量相协调。尽管事件在您的目标(您的 SVG)上,但 MouseEvent 属性仅与您的屏幕相关。

    MouseEvent 接口的 moveX 只读属性提供了给定事件和前一个 mousemove 事件之间鼠标指针 X 坐标的差异。换句话说,属性的值是这样计算的:currentEvent.movementX = currentEvent.screenX - previousEvent.screenX。

    来自https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX

    MouseEvent 接口的 screenX 只读属性提供鼠标指针在全局(屏幕)坐标中的水平坐标(偏移量)。

    因此,您要测量的内容是指针在屏幕上以像素为单位的移动,据我所知,您可以 直接测量的唯一内容是指针在屏幕上的移动。就 SVG 的移动矢量而言,使这项工作发挥作用的唯一方法是将屏幕上的移动转换为与缩放的 SVG 相关的尺寸。

    我最初的想法是,您将能够计算 SVG 对象的缩放,使用它的视图框和它在屏幕上的实际宽度的某种组合。 当然,最初看起来合理的做法并不合理。这种方法行不通,如果它看起来纯属偶然的话。

    但事实证明,解决方案本质上是在您接近鼠标移动时使用您在缩放中使用的相同类型的代码。 .getScreenCTM().inverse() 函数正是您再次需要的。但是,您需要通过比较 SVG 上的两个点来找出屏幕上的距离在 SVG 中转换为什么,而不是试图在 SVG 上找到一个点来工作。

    我在这里提供的不一定是最佳解决方案,但希望有助于解释并为您提供进一步的工作......

    function dragger(event) {
        var target = document.getElementById('color-wheel');
        var coords = parseViewBox(target);
    
    
        //Get an initial point in the SVG to start measuring from
        var start_pt = target.createSVGPoint();
    
        start_pt.x = 0;
        start_pt.y = 0;
    
        var svgcoord = start_pt.matrixTransform(target.getScreenCTM().inverse());
    
    
        //Create a point within the same SVG that is equivalent to 
        //the px movement by the pointer
        var comparison_pt = target.createSVGPoint();
    
        comparison_pt.x = event.movementX;
        comparison_pt.y = event.movementY;
    
        var svgcoord_plus_movement = comparison_pt.matrixTransform(target.getScreenCTM().inverse());
    
    
        //Use the two SVG points created from screen position values to determine 
        //the in-SVG distance to change coordinates
        coords.x -=  (svgcoord_plus_movement.x - svgcoord.x);
    
    
        //Repeat the above, but for the Y axis
        coords.y -= (svgcoord_plus_movement.y - svgcoord.y);
    
    
        //Deliver the changes to the SVG to update the view
        changeViewBox(target,coords);
    }
    

    对于冗长的答案感到抱歉,但希望它从一开始就足以解释它,以便任何想要找到答案的人都可以了解整个情况,如果他们没有达到你在这个脚本中的程度。

    【讨论】:

      【解决方案3】:

      MouseEvent,我们有clientXmovememntX。综合起来,我们可以推断出我们最后的位置。然后我们可以对当前位置进行变换,然后从上一个位置的变换中减去它:

      element.onpointermove = e => {
          const { clientX, clientY, movementX, movementY } = e;
          const DOM_pt = svg.createSVGPoint();
          DOM_pt.x = clientX;
          DOM_pt.y = clientY;
          const { x, y } = DOM_pt.matrixTransform(svgs[i].getScreenCTM().inverse());
          DOM_pt.x += movementX;
          DOM_pt.y += movementY;
          const { x: last_x, y: last_y } = DOM_pt.matrixTransform(svgs[i].getScreenCTM().inverse());
          const dx = last_x - x;
          const dy = last_y - y;
          // TODO: use dx & dy
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-02
        • 1970-01-01
        • 2021-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-24
        • 2019-11-04
        相关资源
        最近更新 更多