javascript - 使用 Snap.svg (Javascript) 不可重叠的 Draggables

标签 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 添加另一个碰撞检测 block ,但我认为这会大大降低引擎速度。我的碰撞检测似乎过于复杂。因此,我希望有一种更有效的方法来测试碰撞。

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

如能就这三个问题提供任何帮助,我们将不胜感激。

谢谢!

最佳答案

抱歉,我只能提供使用 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>

关于javascript - 使用 Snap.svg (Javascript) 不可重叠的 Draggables,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38099780/

相关文章:

javascript - 在 PDF 文件上打印生成的 QR 码

python - 了解碰撞列表中哪个矩形发生了碰撞

javascript - 如何将 Backbone View 事件哈希定义为函数?

javascript - 从 ng-repeat 中删除重复项

javascript - 防止点击内部链接时点击外部li

javascript - 使用 SVG 生成圆形按钮

JavaScript RegEx 匹配 url 路径

javascript - 无法在 React 和 TS 中使用 Parcel 找到 svgs Assets

c# - 如何预测船舶与人体在二维影响范围内的相遇

algorithm - 什么是好的、简单的、仅限二维矩形的碰撞检测算法?