2016-06-29 16 views
1

これは初めてスタックオーバーフローで質問を投稿したので、私の質問をどのように改善できるかについて自由にコメントしてください。Snap.svg(JavaScript)を使用したアンラップ不可ドラッグドラッグ可能

私は重ならない2つのSVG四角形のドラッグ可能なものを作ろうとしています。これを行うために、Snap.svgを使用して要素をドラッグし、Snap関数の.isBBoxIntersectユーティリティメソッドを使用して、移動関数の呼び出しごとにバウンディングボックスを取得して衝突するかどうかを確認しました。彼らが衝突した場合、私はそれらが重複しないようにして、それぞれのオブジェクトを他のものに頼りにしないようにします。現在ドラッグされているオブジェクトは、衝突が再びfalseを返すまで、特定の軸上を移動します。 I私がここで何をしたいのいくつかの基本的なコードを持っている:

<html> 
<head> 
<title> 
</title> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script> 

</head> 
<body> 

<script> 
var s = Snap(600,500); 

var rect = s.rect(0,0,40,40); 
var rectr = s.rect(400,90,50,50); 
var b=0; 
var c=0; 
var isInter; 
var move = function(dx,dy, x, y, event) { 

var b1 = rect.getBBox(); 
var b2 = rectr.getBBox(); 
isInter = Snap.path.isBBoxIntersect(b1, b2); 

if (isInter==false) { 
b=dx; 
c=dy; 
} 

if (isInter==true) { 

if (b1.y2==b2.y&&b1.x2==b2.x||b1.x==b2.x2&&b1.y2==b2.y){c=b2.y-b1.h, b=dx 
} 
else if (b1.x==b2.x2&&b1.y==b2.y2||b1.x2==b2.x&&b1.y==b2.y2){c=b2.y2; b=dx;} 
else if (b1.y2==b2.y){(dy>=b2.y-b1.h) ? (c=b2.y-b1.h, b=dx): (b=dx, c=dy);} 
else if (b1.y==b2.y2){(dy<=b1.y) ? (c=b2.y2, b=dx):(b=dx,c=dy);} 
else if (b1.x2==b2.x){(dx>=b1.x) ? (b=b2.x-b1.width, c=dy):(b=dx, c=dy);} 
else if (b1.x==b2.x2){(dx<=b1.x2) ? (b=b2.x2, c=dy):(b=dx, c=dy);} 
else {b=dx; c=dy;} 

} 

this.attr({ 
transform: this.data('origTransform') + ((this.data('origTransform')) ? "t": "T") + [b,c] 
}); 

} 

var start = function() { 
     this.data('origTransform', this.transform().local); 
b=0; 
c=0; 
} 


rect.drag(move, start); 
circle.drag(move, start); 
</script> 
</body> 
</html> 

これらは、思いついた三つの主要な問題である:あなたが速すぎてドラッグすると、エンジンが追いついていないこと

  1. 、およびドラッグ可能な部分が重なり合って表示されます。どのくらい速くドラッグされてもオーバーラップを防ぐ方法が望まれます。

  2. 衝突は、rectrでドラッグされたときにrectに対してのみ機能します。私は簡単に直角上のrectrの衝突検出の別のブロックを追加することができますが、私はそれがエンジンをあまりにも遅くすると思います。私の衝突検出は非常に複雑に思えます。したがって、私は衝突をテストするより効率的な方法を望んでいます。

  3. rectが最初にドラッグされ、次にrectがrectrでドラッグされると、衝突検出が完全に失敗します。これは.getBBox()の問題かもしれませんが、私は確かに言うことができません。

これらの3つの問題についての助力は非常に高く評価されます。

ありがとうございました!

+0

B、Cは何をしているなど、より良いコメントせずに、あなたのコード内のロジックを通して読みにくいです。 b1.y == b2.yのようなものでbboxをテストしたいのですか?彼らが等しいかどうかは分かりませんが、ブロッキングポイントを見つけるには> =または<=などであるかどうかだけをテストします。たぶん私はかなりコードを理解していないだろう。 – Ian

+0

重複を完全に防止するためのほとんどのアプローチは、通常、動きに望ましくない「詰まり」をもたらします。衝突した要素の不透明度を変更するだけで、衝突がfalseの場合は不透明度= 1が返されます。これは、要素が衝突したときにドラッグ中の「穏やかな」ユーザフィードバックを提供します。 –

+0

@Ian私は、rectがそれが衝突したrectrの側に "スティック"することを望んでいました。 > =または<=演算子を使用した場合、rect365の別の側と衝突した場合でも、<= or > =演算子を使用して、条件付きにテレポートする可能性があります。 –

答えて

0

OK、SVGのネイティブメソッドとJavaScriptを使用した例を見てみましょう。 この例では、rect 'stick'をもう一方に付けます。どのくらい多くの要素が関わっても、ドラッグは低下しません。 次のステップは、矩形が一緒に詰まった後の処理を決定することです。

<head> 
 
    <title>Untitled</title> 
 
</head> 
 

 
<body> 
 
<svg width="800" height="800" onmousedown="startDrag(evt)" onmousemove="drag(evt)" onmouseup="endDrag()"> 
 
<rect class="dragTarget" id=rect1 x=200 y=200 width=50 height=90 fill=red /> 
 
<rect class="dragTarget" id=rect2 x=400 y=400 width=50 height=90 fill=blue /> 
 
</svg> 
 
</body> 
 
<script> 
 
function intersectRect(shape1, shape2) { 
 
    var r1 = shape1.getBoundingClientRect(); //BOUNDING BOX OF THE FIRST OBJECT 
 
    var r2 = shape2.getBoundingClientRect(); //BOUNDING BOX OF THE SECOND OBJECT 
 

 
    //CHECK IF THE TWO BOUNDING BOXES OVERLAP 
 
    return !(r2.left > r1.right || 
 
      r2.right < r1.left || 
 
      r2.top > r1.bottom || 
 
      r2.bottom < r1.top); 
 
} 
 
var TransformRequestObj 
 
var TransList 
 
var DragTarget=null; 
 
var Dragging = false; 
 
var OffsetX = 0; 
 
var OffsetY = 0; 
 
//---mouse down over element--- 
 
function startDrag(evt) 
 
{ 
 
\t if(!Dragging) //---prevents dragging conflicts on other draggable elements--- 
 
\t { 
 
\t \t if(evt.target.getAttribute("class")=="dragTarget") 
 
\t \t { 
 
\t \t \t DragTarget = evt.target; 
 
\t \t \t DragTarget.setAttribute("style","cursor:move") 
 
\t \t \t //---reference point to its respective viewport-- 
 
\t \t \t var pnt = DragTarget.ownerSVGElement.createSVGPoint(); 
 
\t \t \t pnt.x = evt.clientX; 
 
\t \t \t pnt.y = evt.clientY; 
 
\t \t \t //---elements transformed and/or in different(svg) viewports--- 
 
\t \t \t var sCTM = DragTarget.getScreenCTM(); 
 
\t \t \t var Pnt = pnt.matrixTransform(sCTM.inverse()); 
 

 
\t \t \t TransformRequestObj = DragTarget.ownerSVGElement.createSVGTransform() 
 
\t \t \t //---attach new or existing transform to element, init its transform list--- 
 
\t \t \t var myTransListAnim=DragTarget.transform 
 
\t \t \t TransList=myTransListAnim.baseVal 
 

 
\t \t \t OffsetX = Pnt.x 
 
\t \t \t OffsetY = Pnt.y 
 

 
\t \t \t Dragging=true; 
 
\t \t } 
 
\t } 
 
} 
 
//---mouse move--- 
 
function drag(evt) 
 
{ 
 

 
\t if(Dragging) 
 
\t { 
 
\t   if(intersectRect(rect1, rect2)==false) 
 
      { 
 
     \t \t var pnt = DragTarget.ownerSVGElement.createSVGPoint(); 
 
     \t \t pnt.x = evt.clientX; 
 
     \t \t pnt.y = evt.clientY; 
 
     \t \t //---elements in different(svg) viewports, and/or transformed --- 
 
     \t \t var sCTM = DragTarget.getScreenCTM(); 
 
     \t \t var Pnt = pnt.matrixTransform(sCTM.inverse()); 
 
     \t \t Pnt.x -= OffsetX; 
 
     \t \t Pnt.y -= OffsetY; 
 

 
     \t \t TransformRequestObj.setTranslate(Pnt.x,Pnt.y) 
 
     \t \t TransList.appendItem(TransformRequestObj) 
 
     \t \t TransList.consolidate() 
 
      } 
 
\t } 
 
} 
 
//--mouse up--- 
 
function endDrag() 
 
{ 
 
    Dragging = false; 
 
    DragTarget.setAttribute("style","cursor:default") 
 
} 
 
</script>

+0

私はデモが行ったのと同じように、2つのオブジェクトが衝突してもドラッグが有効になることを期待していました。この場合、rectは他のrectに詰め込まれ、完全に移動不能になるのではなく、上または下に衝突するとx軸上でドラッグ可能になり、左右にある場合にはy軸だけに引っかかります。私は、ポインタ(マウス)がもう一度それに達することができる領域に移動するまで、この挙動を一定のままにします。コメントは長すぎるので、私は別のものに続けます。 –

+0

例では、あなたの例の赤い矩形が上からの青い矩形と衝突すると、ポインタは青の矩形の左側またはxの下のx値を移動するまでx軸上を移動できます。またはポインタのy値が赤い矩形の中心よりも上にある場合。この種のロジックは、両方の矩形の4辺すべてに適用されます。 jQuery UIのドラッグ可能な障害物を想像してください。そこでは、両方のrectがドラッグ可能であり、SVGにある以外は互いの障害物です。 –

2

まあ、私は申し訳ありませんが、Javascriptを使用して代替手段を提供することができます。多分、これは将来いつかあなたを助けるでしょう。

この例では、ランダムに配置された50個のsvg図形を作成します。各図形はドラッグ可能です。ドラッグされた図形の境界ボックスが他の図形と交差する場合、その不透明度は、ドラッグされた図形が交差範囲外に移動するまで変更されます。

幸いです。

<head> 
 
    <title>Untitled</title> 
 
</head> 
 
<body onLoad=svgGLOB(50,800,800,30)> 
 
<div style=font-family:arial> 
 
Fifty(50) svg shapes are created and randomly located. Each shape is draggable. If the dragged shape's bounding box intersects another shape, its opacity is changed until the dragged shape moves out of intersect range. 
 
</div> 
 
<div id=svgDiv style='width:800px;height:800px;border:1px solid black'> 
 
<svg id=mySVG width="800" height="800" onmousedown="startDrag(evt)" onmousemove="drag(evt)" onmouseup="endDrag()"></svg> 
 
</div> 
 
</body> 
 
<script> 
 
function intersectShape(target) 
 
{ 
 
    var r1 = target.getBoundingClientRect(); //BOUNDING BOX OF THE TARGET OBJECT 
 

 
    for(k=0;k<globG.childNodes.length;k++) 
 
    { 
 
     var shape=globG.childNodes.item(k) 
 
     if(shape!=target) 
 
     { 
 
      var r2=shape.getBoundingClientRect(); 
 

 
      //CHECK IF ANY TWO BOUNDING BOXES OVERLAP 
 
      if(!(r2.left > r1.right || 
 
      r2.right < r1.left || 
 
      r2.top > r1.bottom || 
 
      r2.bottom < r1.top)) 
 
       shape.setAttribute("opacity",.5) 
 
      else 
 
       shape.setAttribute("opacity",1) 
 
     } 
 
    } 
 
} 
 
var TransformRequestObj 
 
var TransList 
 
var DragTarget=null; 
 
var Dragging = false; 
 
var OffsetX = 0; 
 
var OffsetY = 0; 
 

 
//---mouse down over element--- 
 
function startDrag(evt) 
 
{ 
 
\t if(!Dragging) //---prevents dragging conflicts on other draggable elements--- 
 
\t { 
 
\t \t if(evt.target.getAttribute("class")=="dragTarget") 
 
\t \t { 
 
\t \t \t DragTarget = evt.target; 
 
\t \t \t DragTarget.setAttribute("style","cursor:move") 
 
\t \t \t //---reference point to its respective viewport-- 
 
\t \t \t var pnt = DragTarget.ownerSVGElement.createSVGPoint(); 
 
\t \t \t pnt.x = evt.clientX; 
 
\t \t \t pnt.y = evt.clientY; 
 
\t \t \t //---elements transformed and/or in different(svg) viewports--- 
 
\t \t \t var sCTM = DragTarget.getScreenCTM(); 
 
\t \t \t var Pnt = pnt.matrixTransform(sCTM.inverse()); 
 

 
\t \t \t TransformRequestObj = DragTarget.ownerSVGElement.createSVGTransform() 
 
\t \t \t //---attach new or existing transform to element, init its transform list--- 
 
\t \t \t var myTransListAnim=DragTarget.transform 
 
\t \t \t TransList=myTransListAnim.baseVal 
 

 
\t \t \t OffsetX = Pnt.x 
 
\t \t \t OffsetY = Pnt.y 
 

 
\t \t \t Dragging=true; 
 
\t \t } 
 

 
\t } 
 
} 
 
//---mouse move--- 
 
function drag(evt) 
 
{ 
 
    if(Dragging) 
 
    { 
 
     var pnt = DragTarget.ownerSVGElement.createSVGPoint(); 
 
     pnt.x = evt.clientX; 
 
     pnt.y = evt.clientY; 
 
     //---elements in different(svg) viewports, and/or transformed --- 
 
     var sCTM = DragTarget.getScreenCTM(); 
 
     var Pnt = pnt.matrixTransform(sCTM.inverse()); 
 
     Pnt.x -= OffsetX; 
 
     Pnt.y -= OffsetY; 
 

 
     TransformRequestObj.setTranslate(Pnt.x,Pnt.y) 
 
     TransList.appendItem(TransformRequestObj) 
 
     TransList.consolidate() 
 

 
     intersectShape(DragTarget) 
 
    } 
 
} 
 
//--mouse up--- 
 
function endDrag() 
 
{ 
 
    Dragging = false; 
 
    DragTarget.setAttribute("style","cursor:default") 
 
} 
 

 
//==================add a bunch of SVG elements============ 
 
//---onload: svgGLOB(50,800,800,30)--- 
 
function svgGLOB(elems,svgWidth,svgHeight,elemSize) 
 
{ 
 
\t /* ---fill empty inline SVG element--- 
 
\t \t <div id="svgDiv"><svg id="mySVG" /></div> 
 
\t */ 
 
\t var NS="http://www.w3.org/2000/svg" 
 
\t mySVG.setAttribute("width",svgWidth) 
 
\t mySVG.setAttribute("height",svgHeight) 
 
\t svgDiv.style.width=svgWidth+"px" 
 
\t svgDiv.style.height=svgHeight+"px" 
 

 
\t var globG=document.createElementNS(NS,"g") 
 
\t globG.id="globG" 
 
\t globG.setAttribute("stroke","black") 
 
\t globG.setAttribute("stroke-width",1) 
 
\t mySVG.appendChild(globG) 
 

 
\t var points=randomPoints(elems,svgWidth,svgHeight,elemSize) 
 
\t var n=points.length 
 
\t var circleCnt=0 
 
\t var ellipseCnt=0 
 
\t var rectCnt=0 
 
\t var polygonCnt=0 
 

 
\t var RandomElems=[] 
 
\t RandomElems[0]="circle" 
 
\t RandomElems[1]="rect" 
 
\t RandomElems[2]="ellipse" 
 
\t RandomElems[3]="polygon_3" 
 
\t RandomElems[4]="polygon_4" 
 
\t RandomElems[5]="polygon_5" 
 
\t RandomElems[6]="polygon_6" 
 
\t RandomElems[7]="polygon_7" 
 
\t RandomElems[8]="polygon_8" 
 
\t RandomElems[9]="polygon_9" 
 
\t RandomElems[10]="polygon_10" 
 
\t RandomElems[11]="polygon_11" 
 
\t RandomElems[12]="polygon_12" 
 

 
\t for(var k=0;k<n;k++) 
 
\t { 
 
\t \t var rand=rdm(0,12) 
 
\t \t var elemStr=RandomElems[rand] 
 

 
\t \t if(!elemStr.indexOf("_")) 
 
\t \t \t var elemSt=elemStr 
 
\t \t else 
 
\t \t \t var elemSt=elemStr.split("_")[0] 
 

 
\t \t var elem=document.createElementNS(NS,elemSt) 
 

 
\t \t if(elemSt=="circle") 
 
\t \t { 
 
\t \t \t elem.setAttribute("r",elemSize) 
 
\t \t \t elem.setAttribute("fill",rcolor()) 
 
\t \t \t elem.setAttribute("cx",points[k][0]) 
 
\t \t \t elem.setAttribute("cy",points[k][1]) 
 
\t \t \t elem.id=elemSt+(circleCnt++) 
 
\t \t } 
 
\t \t else if(elemSt=="ellipse") 
 
\t \t { 
 
\t \t \t elem.setAttribute("rx",elemSize) 
 
\t \t \t elem.setAttribute("ry",elemSize/2) 
 
\t \t \t elem.setAttribute("fill",rcolor()) 
 
\t \t \t elem.setAttribute("cx",points[k][0]) 
 
\t \t \t elem.setAttribute("cy",points[k][1]) 
 
\t \t \t elem.id=elemSt+(ellipseCnt++) 
 
\t \t } 
 
\t \t else if(elemSt=="rect") 
 
\t \t { 
 
\t \t \t elem.setAttribute("width",elemSize) 
 
\t \t \t elem.setAttribute("height",elemSize) 
 
\t \t \t elem.setAttribute("fill",rcolor()) 
 
\t \t \t elem.setAttribute("x",points[k][0]) 
 
\t \t \t elem.setAttribute("y",points[k][1]) 
 
\t \t \t elem.id=elemSt+(rectCnt++) 
 
\t \t } 
 
\t \t else if(elemSt=="polygon") 
 
\t \t { 
 
\t \t \t var pgonSides=parseInt(elemStr.split("_")[1]) 
 
\t \t \t var pgonPnts=polygon(pgonSides,elemSize,points[k][0],points[k][1]) 
 
\t \t \t elem.setAttribute("fill",rcolor()) 
 
\t \t \t elem.setAttribute("points",pgonPnts.join()) 
 
\t \t \t elem.id=elemSt+(polygonCnt++) 
 
\t \t } 
 
     elem.setAttribute("class","dragTarget") 
 
\t \t globG.appendChild(elem) 
 
\t } 
 

 
\t //---obtain a random whole number from a thru b--- 
 
\t function rdm(a,b) 
 
\t { 
 
\t \t return a + Math.floor(Math.random()*(b-a+1)); 
 
\t } 
 

 
\t function randomPoints(elems,svgWidth,svgHeight,elemSize) 
 
\t { 
 
\t \t //--return format:[ [x,y],[x,y],,, ] 
 
\t \t //---Generate random points--- 
 
\t \t function times(n, fn) 
 
\t \t { 
 
\t \t \t var a = [], i; 
 
\t \t \t for (i = 0; i < n; i++) { 
 
\t \t \t a.push(fn(i)); 
 
\t \t \t } 
 
\t \t \t return a; 
 
\t \t } 
 
\t \t var width=svgWidth-2*elemSize 
 
\t \t var height=svgHeight-2*elemSize 
 

 
\t \t return \t RandomPnts = times(elems, function() { return [Math.floor(width * Math.random()) + elemSize, Math.floor(height * Math.random()) + elemSize] }); 
 
\t } 
 
    //---random color--- 
 
\t function rcolor() 
 
\t { 
 
\t \t var letters = 'ABCDEF'.split(''); 
 
\t \t var color = '#'; 
 
\t \t for (var i = 0; i < 6; i++) 
 
\t \t { 
 
\t \t \t color += letters[Math.round(Math.random() * 15)]; 
 
\t \t } 
 
\t \t return color; 
 
\t } 
 
\t function polygon(vCnt,radius,centerX,centerY) 
 
\t { 
 
\t \t var myPoints=[] 
 
\t \t var polyXPts  = Array(vCnt); 
 
\t \t var polyYPts  = Array(vCnt); 
 
\t \t var vertexAngle = 360/vCnt; 
 
\t \t //---init polygon points processor--- 
 
\t \t for(var v=0; v<vCnt; v++) 
 
\t \t { 
 
\t \t \t theAngle = (v*vertexAngle)*Math.PI/180; 
 
\t \t \t polyXPts[v] = radius*Math.cos(theAngle); 
 
\t \t \t polyYPts[v] = -radius*Math.sin(theAngle); 
 
\t \t } 
 
\t \t //--note points are CCW--- 
 
\t \t for(var v=0;v<vCnt; v++) 
 
\t \t { 
 
\t \t \t var point=[centerX+polyXPts[v],centerY+polyYPts[v]] 
 
\t \t \t myPoints.push(point) 
 
\t \t } 
 
\t \t return myPoints 
 
\t } 
 
} 
 
</script>

+0

答えが私の問題を完全に解決したわけではありませんが、少なくとも私が以前知らなかったSVGを操作することについては多くのことを学びました。申し訳ありませんが、私は少なくともupvote ...私の評判は低すぎることができませんでした。だから、私はあなたに感謝するしかありません。 :) –

関連する問題