2017-01-06 5 views
0

私はSVG要素をドラッグして純粋な(外部ライブラリなし)で遊んでいました。YES/NO - 純粋なSVGツールでマウスのドラッグを改善する方法はありますか?

すべての作品一般に

が、高速で移動、マウスでこの厄介な問題がある: - ユーザーmousedowns近いその端 にドラッグ可能なSVG要素 - そして、ドラッグ(のMouseMove)などドラッグ早すぎる - マウス「失うには「ドラッグ可能

ここでの問題は、より詳細に説明されています http://www.svgopen.org/2005/papers/AdvancedMouseEventModelForSVG-1/index.html#S3.2 。また、ここで著者がマウスアウトイベントを活用してUXを修正しようとした: http://nuclearprojects.com/blog/svg-click-and-drag-object-with-mouse-code/

私はここで上記のコードをコピー:を

私が持っている質問です:

は、マウスがあまりにも速く移動しながら、SVG要素の、このような「損失」を防ぐために(純粋なSVGが提供する)他の方法はありませんか?

これを解決しようとする私の試みは次のとおりです。 - ドラッグアウトを終了することなくmouseoutイベントが発生したことを(何とか)検出します。 - もしそうなら(私たちは並べ替えを検出しました)、現在のマウス位置でSVG要素を再接続します。

これが機能しない理由はありますか?

コード:あなたがイベントを登録し、あなたのコードの最も重要な部分が欠落している

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

+0

'evt.clientY - clickY'これらは法的javascriptのマイナス...答えを –

答えて

2

、すなわちどのようにまたはより具体的にどの要素に。

基本的ににこの処理を行わないと、最も外側のsvg要素にmousemoveイベントとmouseupイベントが登録されます。をドラッグする要素ではありません。

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

ドラッグを開始するときは、イベントをsvg要素に登録し、終了するとイベントを登録解除します。

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

現在ドラッグしている要素を保存する必要があるため、他のイベントハンドラで使用できます。

さらに、ドラッグされた要素の下のマウスイベントに反応するように、ドラッグされた 要素のポインタイベントを「なし」に設定することができます(f.e.ドロップターゲットの検索...)

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

が、ドラッグが行われたときがある

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>

より高度な

バック賢明な何かにそれを設定することを忘れないでくださいまだ2つこのコードではあまりうまくいきません。

  1. viewBoxed SVGや 変換された親の要素では機能しません。
  2. すべてのグローバルは悪いコーディング慣行です。

ここにこれらを修正する方法があります。 Nr。 1は、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この実装を参照してください。

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>

+0

ちょっと感謝していません。リスナーを「動的に」割り当てることが重要な理由について理解してもらえますか?これは、粘着性のある、高速マウスの移動 "切断"の問題を解決するのだろうか?イベントハンドラとしての –

+0

がsvg要素にある場合、常にmoveイベントが発生します。静的ハンドラでは、ドラッグコードの周りにif文を置くことができますが、必要でない場合は使用しない方が一般的です。この方法でもっと親しみやすくなり、プレゼンテーションからコードを分離します。あなたはSVGコード自体に触れることなくSVG上でこのコードを使用することができます... "粘着性のある" "切断"問題を解決するものは、ハンドラが最も外側のsvg要素に登録され、ドラッグする要素ではありません。私は答えのこれらの部分を強調表示しています... –

+0

あなたのコードスニペットは本当にうまくいきます。だから私は、動的リスナーのもの+他の項目はSVGで非常にまともなマウスの動作を得ることが可能になると思います! –

関連する問題