是/否 - 有没有办法用纯 SVG 工具改进鼠标拖动?

Ser*_*nko 2 mouse svg drag

所以我花了一些时间尝试纯(无外部库)SVG 元素拖动。

\n\n

一般来说,一切都有效,但是快速移动的鼠标存在这个令人讨厌的问题:\n-当用户将可拖动的 SVG 元素靠近其边缘时\n-然后拖动(鼠标移动)这样的可拖动速度太快\n-鼠标“丢失”可拖动的

\n\n

这里更详细地描述了该问题:\n http://www.svgopen.org/2005/papers/AdvancedMouseEventModelForSVG-1/index.html#S3.2 \n此外,作者尝试通过利用 mouseout 事件来修复用户体验: \n http://nuclearprojects.com/blog/svg-click-and-drag-object-with-mouse-code/

\n\n

我在这里复制了上面的代码片段: http ://codepen.io/cmer41k/pen/zNGwpa

\n\n

我的问题是:

\n\n

是否没有其他方法(由纯 SVG 提供)来防止鼠标移动太快时 SVG 元素的这种“丢失”?

\n\n

我尝试解决这个问题是: \n- 检测(以某种方式)在未完成拖动的情况下发生了 mouseout 事件。\n- 如果是这样(我们有点检测到“断开连接”)- 将 SVG 元素与当前鼠标位置重新连接。

\n\n

这有什么行不通的原因吗?

\n\n

代码:

\n\n

\n\n

\r\n
\r\n
    var click=false; // flag to indicate when shape has been clicked\r\n    var clickX, clickY; // stores cursor location upon first click\r\n    var moveX=0, moveY=0; // keeps track of overall transformation\r\n    var lastMoveX=0, lastMoveY=0; // stores previous transformation (move)\r\n    function mouseDown(evt){\r\n        evt.preventDefault(); // Needed for Firefox to allow dragging correctly\r\n        click=true;\r\n        clickX = evt.clientX; \r\n        clickY = evt.clientY;\r\n        evt.target.setAttribute("fill","green");\r\n    }\r\n\r\n    function move(evt){\r\n        evt.preventDefault();\r\n        if(click){\r\n            moveX = lastMoveX + ( evt.clientX \xe2\x80\x93 clickX );\r\n            moveY = lastMoveY + ( evt.clientY \xe2\x80\x93 clickY );\r\n\r\n            evt.target.setAttribute("transform", "translate(" + moveX + "," + moveY + ")");\r\n        }\r\n    }\r\n\r\n    function endMove(evt){\r\n        click=false;\r\n        lastMoveX = moveX;\r\n        lastMoveY = moveY;\r\n        evt.target.setAttribute("fill","gray");\r\n    }
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

Hol*_*ill 6

代码中最重要的部分丢失了,即如何注册事件,或者更具体地说,在哪个元素上注册事件。

为了防止这个问题,你基本上要做的就是在最外面的 svg 元素上注册 mousemove 和 mouseup 事件,而不是在你想要拖动的元素上

svg.addEventListener("mousemove", move)
svg.addEventListener("mouseup", endMove)
Run Code Online (Sandbox Code Playgroud)

开始拖动时,在 svg 元素上注册事件,完成后取消注册它们。

svg.removeEventListener("mousemove", move)
svg.removeListener("mouseup", endMove)
Run Code Online (Sandbox Code Playgroud)

您必须存储当前拖动的元素,以便在其他事件处理程序中可用。

我另外做的是将拖动元素上的指针事件设置为“无”,以便您可以对拖动元素下方的鼠标事件做出反应(找到放置目标...)

evt.target.setAttribute("pointer-events", "none")
Run Code Online (Sandbox Code Playgroud)

但不要忘记在拖动完成后将其设置回合理的值

evt.target.setAttribute("pointer-events", "all")
Run Code Online (Sandbox Code Playgroud)

svg.addEventListener("mousemove", move)
svg.addEventListener("mouseup", endMove)
Run Code Online (Sandbox Code Playgroud)
svg.removeEventListener("mousemove", move)
svg.removeListener("mouseup", endMove)
Run Code Online (Sandbox Code Playgroud)

更先进

这段代码还有两点不太好。

  1. 它不适用于 viewBoxed SVG,也不适用于转换后的父级内部的元素。
  2. 所有的全局变量都是不好的编码习惯。

以下是解决这些问题的方法:Nr。1 通过使用 getScreenCTM(CTM = 当前变换矩阵)的逆将鼠标坐标转换为本地坐标来求解。

function globalToLocalCoords(x, y) {
    var p = elem.ownerSVGElement.createSVGPoint()
    var m = elem.parentNode.getScreenCTM()
    p.x = x
    p.y = y
    return p.matrixTransform(m.inverse())
  }
Run Code Online (Sandbox Code Playgroud)

对于nr。2 看这个实现:

evt.target.setAttribute("pointer-events", "none")
Run Code Online (Sandbox Code Playgroud)
evt.target.setAttribute("pointer-events", "all")
Run Code Online (Sandbox Code Playgroud)
var click = false; // flag to indicate when shape has been clicked
var clickX, clickY; // stores cursor location upon first click
var moveX = 0,
  moveY = 0; // keeps track of overall transformation
var lastMoveX = 0,
  lastMoveY = 0; // stores previous transformation (move)
var currentTarget = null

function mouseDown(evt) {
  evt.preventDefault(); // Needed for Firefox to allow dragging correctly
  click = true;
  clickX = evt.clientX;
  clickY = evt.clientY;
  evt.target.setAttribute("fill", "green");
  // register move events on outermost SVG Element
  currentTarget = evt.target
  svg.addEventListener("mousemove", move)
  svg.addEventListener("mouseup", endMove)
  evt.target.setAttribute("pointer-events", "none")
}

function move(evt) {
  evt.preventDefault();
  if (click) {
    moveX = lastMoveX + (evt.clientX - clickX);
    moveY = lastMoveY + (evt.clientY - clickY);
    currentTarget.setAttribute("transform", "translate(" + moveX + "," + moveY + ")");
  }
}

function endMove(evt) {
  click = false;
  lastMoveX = moveX;
  lastMoveY = moveY;
  currentTarget.setAttribute("fill", "gray");
  svg.removeEventListener("mousemove", move)
  svg.removeEventListener("mouseup", endMove)
  currentTarget.setAttribute("pointer-events", "all")
}
Run Code Online (Sandbox Code Playgroud)