不可重叠可拖放与Snap.svg (Javascript)

Unoverlappable Draggables with Snap.svg (Javascript)

本文关键字:Javascript svg 重叠 拖放 Snap      更新时间:2023-09-26

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

我试图使两个SVG矩形拖拽不能重叠。为此,我使用Snap.svg来拖动元素,并使用Snap API中的. isbboxintersect实用程序方法,在每次调用move函数时获取它们的边界框,以查看它们是否发生碰撞。如果它们发生碰撞,我要确保它们不会重叠,从而使每个对象无法通过另一个对象。当前被拖动的对象将沿着某个轴移动,直到碰撞再次返回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时起作用。我可以很容易地为矩形添加另一个碰撞检测块,但我认为这会使引擎速度变慢太多。我的碰撞检测似乎过于复杂。因此,我希望有一种更有效的方法来测试碰撞。

  3. 如果rectr先被拖拽,然后rect被拖拽到rectr上,碰撞检测完全失败。这可能是一个问题。getbbox(),但我不能肯定地说。

在这三个问题上任何帮助都将是非常感激的。

谢谢!

抱歉,我只能提供另一种使用Javascript的方法。也许这在将来的某个时候会对你有所帮助。

这个例子创建了50(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>

好的,让我们从使用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>