是/否-是否可以使用纯SVG工具改善鼠标拖动? [英] YES/NO - is there a way to improve mouse dragging with pure SVG tools?

查看:76
本文介绍了是/否-是否可以使用纯SVG工具改善鼠标拖动?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我花了一些时间玩弄纯(无外部库)SVG元素.

So I was spending some time playing around with pure (no external libraries) SVG elements dragging.

一般来说,所有方法都有效,但是对于快速移动的鼠标来说,这是一个令人讨厌的问题: -当用户将鼠标拖动到靠近其边缘的可拖动SVG元素时 -然后拖动(mousemove)这样的拖动速度太快 -鼠标丢失"可拖动的对象

In general all works, but there is this nasty issue for fast moving mouse: - when user mousedowns a draggable SVG element close to its edge - then drags (mousemove) such draggable too fast - the mouse "loses" the draggable

此处详细介绍了该问题: http://www.svgopen.org/2005/papers/AdvancedMouseEventModelForSVG-1/index.html#S3.2 同样在这里,作者尝试通过利用mouseout事件来修复UX: http://nuclearprojects.com/blog/svg单击并拖动带有鼠标代码的对象/

Here the issue is described in more details: http://www.svgopen.org/2005/papers/AdvancedMouseEventModelForSVG-1/index.html#S3.2 Also here the author tried to fix UX by leveraging mouseout event: http://nuclearprojects.com/blog/svg-click-and-drag-object-with-mouse-code/

我在此处复制了上述代码段: http://codepen.io/cmer41k/pen/zNGwpa

I copied the above code snippet here: http://codepen.io/cmer41k/pen/zNGwpa

我的问题是:

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

Is there no other way (provided by pure SVG) to prevent such "loss" of SVG element while mouse moves too fast?

我试图解决的问题是: -在不完成拖动的情况下(以某种方式)检测出发生mouseout事件. -如果是的话(我们检测到断开连接")-用当前鼠标位置重新连接SVG元素.

My attempt to solve this was: - detect (somehow) that mouseout event happened without finishing the dragging. - and if so (we sort of detected "disconnect") - reconnect the SVG element with current mouse position.

有什么理由不能解决这个问题吗?

代码:

    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)
    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");
    }

    function move(evt){
        evt.preventDefault();
        if(click){
            moveX = lastMoveX + ( evt.clientX – clickX );
            moveY = lastMoveY + ( evt.clientY – clickY );

            evt.target.setAttribute("transform", "translate(" + moveX + "," + moveY + ")");
        }
    }

    function endMove(evt){
        click=false;
        lastMoveX = moveX;
        lastMoveY = moveY;
        evt.target.setAttribute("fill","gray");
    }

推荐答案

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

The most important part of your code is missing, namely how or more specifically on which element you register the events.

要预防此问题,基本上要做的是在最外面的svg元素上而不是在要拖动的元素上注册mousemove和mouseup事件.

What you basically do to prevent this problem is to register the mousemove and mouseup events on the outermost svg element, and not on the element you want to drag.

svg.addEventListener("mousemove", move)
svg.addEventListener("mouseup", endMove)

开始拖动时,将事件注册到svg元素上,完成后注销它们.

When starting the drag, register the events on the svg element, and when done unregister them.

svg.removeEventListener("mousemove", move)
svg.removeListener("mouseup", endMove)

您必须存储当前要拖动的元素,因此在其他事件处理程序中可用.

you have to store the element you are currently dragging, so it is available in the other event handlers.

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

what i additionally do is to set pointer-events to "none" on the dragged element so that you can react to mouse events underneath the dragged element (f.e. finding the drop target...)

evt.target.setAttribute("pointer-events", "none")

但不要忘记在完成拖动后将其设置回明智的位置

but don't forget to set it back to something sensible when dragging is done

evt.target.setAttribute("pointer-events", "all")

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")
}

<svg id="svg" width="800" height="600" style="border: 1px solid black; background: #E0FFFF;">
  <rect x="0" y="0" width="800" height="600" fill="none" pointer-events="all" />
  <circle id="mycirc" cx="60" cy="60" r="22" onmousedown="mouseDown(evt)" />
</svg>

更高级

在此代码中还有两件事情不太好.

there are still two things not so well with this code.

  1. 它不适用于viewBoxed SVG或内部元素 转变了父母.
  2. 所有全局变量都是不好的编码习惯.
  1. it does not work for viewBoxed SVGs nor for elements inside transformed parents.
  2. all the globals are bad coding practice.

以下是解决这些问题的方法: Nr.通过使用getScreenCTM的逆函数(CTM = Current Transformation Matrix)将鼠标坐标转换为局部坐标,可以解决1.

here is how to fix those: Nr. 1 is solved by converting mouse coordinates into local coordinates using the inverse of getScreenCTM (CTM = Current Transformation Matrix).

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())
  }

对于nr. 2查看此实现:

For nr. 2 see this implementation:

var dre = document.querySelectorAll(".draggable")
for (var i = 0; i < dre.length; i++) {
  var o = new Draggable(dre[i])
}

function Draggable(elem) {
  this.target = elem
  this.clickPoint = this.target.ownerSVGElement.createSVGPoint()
  this.lastMove = this.target.ownerSVGElement.createSVGPoint()
  this.currentMove = this.target.ownerSVGElement.createSVGPoint()
  this.target.addEventListener("mousedown", this)
  this.handleEvent = function(evt) {
    evt.preventDefault()
    this.clickPoint = globalToLocalCoords(evt.clientX, evt.clientY)
    this.target.classList.add("dragged")
    this.target.setAttribute("pointer-events", "none")
    this.target.ownerSVGElement.addEventListener("mousemove", this.move)
    this.target.ownerSVGElement.addEventListener("mouseup", this.endMove)
  }
  this.move = function(evt) {
    var p = globalToLocalCoords(evt.clientX, evt.clientY)
    this.currentMove.x = this.lastMove.x + (p.x - this.clickPoint.x)
    this.currentMove.y = this.lastMove.y + (p.y - this.clickPoint.y)
    this.target.setAttribute("transform", "translate(" + this.currentMove.x + "," + this.currentMove.y + ")")
  }.bind(this)

  this.endMove = function(evt) {
    this.lastMove.x = this.currentMove.x
    this.lastMove.y = this.currentMove.y
    this.target.classList.remove("dragged")
    this.target.setAttribute("pointer-events", "all")
    this.target.ownerSVGElement.removeEventListener("mousemove", this.move)
    this.target.ownerSVGElement.removeEventListener("mouseup", this.endMove)
  }.bind(this)

  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())
  }
}

.dragged {
  fill-opacity: 0.5;
  stroke-width: 0.5px;
  stroke: black;
  stroke-dasharray: 1 1;
}
.draggable{cursor:move}

<svg id="svg" viewBox="0 0 800 600" style="border: 1px solid black; background: #E0FFFF;">
  <rect x="0" y="0" width="800" height="600" fill="none" pointer-events="all" />
  <circle class="draggable" id="mycirc" cx="60" cy="60" r="22" fill="blue" />
  <g transform="rotate(45,175,75)">
    <rect class="draggable" id="mycirc" x="160" y="60" width="30" height="30" fill="green" />
  </g>
  <g transform="translate(200 200) scale(2 2)">
    <g class="draggable">
      <circle cx="0" cy="0" r="30" fill="yellow"/>
      <text text-anchor="middle" x="0" y="0" fill="red">I'm draggable</text>
    </g>
  </g>
</svg>
<div id="out"></div>

这篇关于是/否-是否可以使用纯SVG工具改善鼠标拖动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆