2016-06-29 13 views
1

Это мой первый вопрос, связанный с переполнением стека, поэтому не стесняйтесь комментировать, как я могу улучшить свой вопрос.Unoverlappable Draggables с Snap.svg (Javascript)

Я пытаюсь сделать два прямоугольных прямоугольника SVG, которые не могут пересекаться. Чтобы сделать это, я использовал Snap.svg, чтобы заставить элементы перетаскивать, и с каждым вызовом функции переместить их ограничивающие поля, чтобы увидеть, сталкиваются ли они, используя метод утилиты .isBBoxIntersect в Snap API. Если они сталкиваются, я хочу убедиться, что они не будут перекрываться, и, таким образом, сделать каждый объект невозможен другим. Объект, который в данный момент перетаскивается, затем перемещается по определенной оси, пока столкновение не вернет false еще раз. У меня есть базовый код, что я хочу здесь:

<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. Я мог бы легко добавить еще один блок обнаружения столкновений для rectr on rect, но я думаю, что это слишком сильно замедлит двигатель WAY. Мое обнаружение столкновения кажется слишком сложным. Таким образом, я надеюсь на более эффективный способ проверки столкновения.

  3. Если прямая перетаскивается сначала, тогда прямая перетаскивается на rectr, обнаружение столкновения полностью не срабатывает. Это может быть проблема .getBBox(), но я не могу сказать наверняка.

Любая помощь по этим трем вопросам была бы чрезвычайно оценена.

Спасибо!

+0

Трудно прочитать логики в вашем коде, не лучше замечаний, например, какие б, в. Вы уверены, что хотите тестировать bbox с такими вещами, как b1.y == b2.y? Не уверен, что вероятность их равных, просто проверьте, есть ли они = = или <= и т.д., чтобы найти точки блокировки. Возможно, я еще не совсем понял код. – Ian

+0

Практически любой подход, позволяющий полностью перекрывать перекрытие, обычно обеспечивает нежелательное «заикание» в движении.Попытайтесь просто изменить непрозрачность сталкивающихся элементов, когда они сталкиваются, возвращая непрозрачность = 1, когда столкновение ложно. Это обеспечивает «нежную» обратную связь с пользователем во время перетаскивания, когда элементы столкнулись. –

+0

@ Ian Я надеялся, что rect «прилипнет» к стороне rectr, на которую он столкнулся. Если я использовал> = или <= операторы, существует возможность прямого «телепортации» в условное выражение с оператором <= or > =, даже если он столкнулся с другой стороной rectr. –

ответ

0

ОК, Начнем с примера, используя собственные методы SVG и javascript. Этот пример имеет либо прямую «палку», либо другую. Перетаскивание не ухудшается независимо от того, сколько элементов задействовано. Следующий шаг - определить, что делать после того, как прямоугольники склеиваются?

<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

Я надеялся, что, как и мое демо, перетаскивание будет включено, даже если оба объекта столкнутся. В этом случае прямоугольник будет застревать на другом прямоугольнике, а не в том смысле, что он становится полностью неподвижным, а только перетаскивается по оси х, если столкновение выше или ниже, а ось y - с левой и с правой стороны. Затем я хочу, чтобы это поведение оставалось постоянным, пока указатель (мышь) не переместится в область, где он может появиться еще раз. Комментарий слишком длинный, поэтому я напишу продолжение в другом. –

+0

Пример: если красный прямоугольник в вашем примере сталкивается с синим прямоугольником сверху, он будет перемещаться только по оси X до тех пор, пока указатель не перемещает значение x под левой стороной или над правой стороной синего прямоугольника, ИЛИ если значение y указателя находится выше центра красного прямоугольника. Такая логика применяется ко всем четырем сторонам обоих прямоугольников. Представьте себе, что jQuery UI перетаскивает и препятствует, когда оба прямоугольника перетаскиваются и препятствия друг к другу, кроме 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, о котором я раньше не знал. Извините, я не мог, по крайней мере, возвысить ... моя репутация слишком низкая. Поэтому я могу только сказать спасибо. :) –

 Смежные вопросы

  • Нет связанных вопросов^_^