所以我花了一些时间尝试纯(无外部库)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 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代码中最重要的部分丢失了,即如何注册事件,或者更具体地说,在哪个元素上注册事件。
为了防止这个问题,你基本上要做的就是在最外面的 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)
更先进
这段代码还有两点不太好。
以下是解决这些问题的方法: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)