使用Snap.svg(Javascript)的不可重写的可拖动

时间:2016-06-29 12:36:50

标签: javascript svg collision-detection draggable snap.svg

这是我第一次在Stack Overflow中发布问题,所以请随时评论我如何改进我的问题。

我正在尝试制作两个不能重叠的SVG矩形可拖动。为此,我使用Snap.svg来拖动元素,并使用移动函数的每次调用获取它们的边界框,以查看它们是否发生碰撞,使用Snap API中的.isBBoxIntersect实用程序方法。如果它们碰撞,我想确保它们不会重叠,从而使每个物体都不能通过另一个物体。然后,当前被拖动的对象将在某个轴上移动,直到碰撞再次返回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上拖动时的rect。我可以轻松地为rect上的rectr添加另一个碰撞检测块,但我认为这会使引擎减速太多。我的碰撞检测似乎过于复杂。因此,我希望有一种更有效的方法来测试碰撞。

  3. 如果先拖动rectr,然后在rectr上拖动rect,则碰撞检测完全失败。这可能是.getBBox()的问题,但我不能肯定地说。

  4. 非常感谢对这三个问题的任何帮助。

    谢谢!

2 个答案:

答案 0 :(得分: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)
{
	if(!Dragging) //---prevents dragging conflicts on other draggable elements---
	{
		if(evt.target.getAttribute("class")=="dragTarget")
		{
			DragTarget = evt.target;
			DragTarget.setAttribute("style","cursor:move")
			//---reference point to its respective viewport--
			var pnt = DragTarget.ownerSVGElement.createSVGPoint();
			pnt.x = evt.clientX;
			pnt.y = evt.clientY;
			//---elements transformed and/or in different(svg) viewports---
			var sCTM = DragTarget.getScreenCTM();
			var Pnt = pnt.matrixTransform(sCTM.inverse());

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

			OffsetX = Pnt.x
			OffsetY = Pnt.y

			Dragging=true;
		}

	}
}
//---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)
{
	/*  ---fill empty inline SVG element---
		<div id="svgDiv"><svg id="mySVG" /></div>
	*/
	var NS="http://www.w3.org/2000/svg"
	mySVG.setAttribute("width",svgWidth)
	mySVG.setAttribute("height",svgHeight)
	svgDiv.style.width=svgWidth+"px"
	svgDiv.style.height=svgHeight+"px"

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

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

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

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

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

		var elem=document.createElementNS(NS,elemSt)

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

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

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

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

答案 1 :(得分: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)
{
	if(!Dragging) //---prevents dragging conflicts on other draggable elements---
	{
		if(evt.target.getAttribute("class")=="dragTarget")
		{
			DragTarget = evt.target;
			DragTarget.setAttribute("style","cursor:move")
			//---reference point to its respective viewport--
			var pnt = DragTarget.ownerSVGElement.createSVGPoint();
			pnt.x = evt.clientX;
			pnt.y = evt.clientY;
			//---elements transformed and/or in different(svg) viewports---
			var sCTM = DragTarget.getScreenCTM();
			var Pnt = pnt.matrixTransform(sCTM.inverse());

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

			OffsetX = Pnt.x
			OffsetY = Pnt.y

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

	if(Dragging)
	{
	        if(intersectRect(rect1, rect2)==false)
           {
        		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()
           }
	}
}
//--mouse up---
function endDrag()
{
    Dragging = false;
    DragTarget.setAttribute("style","cursor:default")
}
</script>