生成和拖动 SVG 元素 - 方法

Posted

技术标签:

【中文标题】生成和拖动 SVG 元素 - 方法【英文标题】:spawn & drag of SVG elements - approach 【发布时间】:2017-03-09 15:51:46 【问题描述】:

我正在学习 javascript/SVG 组合(动画和制作交互式 SVG)。

我想创建一个代码 sn-p,其中菜单元素(“库存”)可以拖动到主屏幕(“画布”),而原始元素将保留在其位置(就像移动副本一样它脱离了原始元素)。

在这里,我尽我所能编写了代码 sn-p: http://codepen.io/cmer41k/pen/f2b5eea274cdde29b0b2dc8a2424a645

所以我设法做了一些事情,但它有问题:

    我可以处理 1 个副本并使其可拖动,但是我不知道如何处理所有这些生成元素的 ID,这会导致拖动问题

    我不明白如何让它无限期地工作(这样它就可以产生任意数量的可拖动到画布上的圆圈)

    画布中的可拖动元素经常重叠,我无法以它们不重叠的方式附加监听器,以便我拖动的元素上的监听器会“通过”那里的任何其他元素传播;(

问题基本上是 - 有人可以建议我应该将逻辑放入这个 sn-p 以便它不那么麻烦。我很确定我在这里遗漏了一些东西;((例如,它不应该那么难,不是吗?)

html

<body>
<svg id="svg"
  
  
    viewbox="0 0 480 800"
    preserveAspectRatio="xMinYMin meet"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
>
    <rect id="canvasBackground"   x="0" y="0"/>
    <rect id="inventoryBackground"   x="0" y="480"/>


<g id="inventory">
<path id="curve4" class="inventory" d="M60,530 A35,35 0 1,1 60,531" />
<path id="curve3" class="inventory" d="M150,530 A35,35 0 1,1 150,531" />
<path id="curve2" class="inventory" d="M240,530 A35,35 0 1,1 240,531" />
<path id="curve1" class="inventory" d="M330,530 A35,35 0 1,1 330,531" />
</g>

<g id="canvas">
</g>

</svg>
</body>

Javascript:

// define meta objects
var drag = null;

// this stores all "curves"-circles
var curves = ;
var canvas = ;
var inventory = ;

window.onload = function() 

        // creates the curve-circles in the object and at their initial x,y coords
        curves.curve1 = document.getElementById("curve1");
        curves.curve1.x = 0;
        curves.curve1.y = 0;
        curves.curve2 = document.getElementById("curve2");
        curves.curve2.x = 0;
        curves.curve2.y = 0;
        curves.curve3 = document.getElementById("curve3");
        curves.curve3.x = 0;
        curves.curve3.y = 0;
        curves.curve4 = document.getElementById("curve4");
        curves.curve4.x = 0;
        curves.curve4.y = 0;
        canvas = document.getElementById("canvas");
        inventory = document.getElementById("inventory");

        // attach events listeners

        AttachListeners();


function AttachListeners() 
    var ttt = document.getElementsByClassName('inventory'), i;
    for (i = 0; i < ttt.length; i++) 
    document.getElementsByClassName("inventory")[i].onmousedown=Drag;
    document.getElementsByClassName("inventory")[i].onmousemove=Drag;
    document.getElementsByClassName("inventory")[i].onmouseup=Drag;
    


// Drag function that needs to be modified;//
function Drag(e) 
        e.stopPropagation();
        var t = e.target, id = t.id, et = e.type;  m = MousePos(e);

            if (!drag && (et == "mousedown")) 

                if (t.className.baseVal=="inventory")  //if its inventory class item, this should get cloned into draggable?
                    copy = t.cloneNode(true);
                    copy.onmousedown=copy.onmousemove=onmouseup=Drag;
                    inventory.insertBefore(copy, inventory.firstChild);
                    drag = t;
                    dPoint = m;
                 
                if (t.className.baseVal=="draggable")    //if its just draggable class - it can be dragged around
                    drag = t;
                    dPoint = m;
                

            
        // drag the spawned/copied draggable element now
            if (drag && (et == "mousemove")) 
                curves[id].x += m.x - dPoint.x;
                curves[id].y += m.y - dPoint.y;
                dPoint = m;
                curves[id].setAttribute("transform", "translate(" +curves[id].x+","+curves[id].y+")");  
            

        // stop drag
            if (drag && (et == "mouseup")) 
                t.className.baseVal="draggable";
                drag = null;
            




// adjust mouse position to the matrix of SVG & screen size
function MousePos(event) 
        var p = svg.createSVGPoint();
        p.x = event.clientX;
        p.y = event.clientY;
        var matrix = svg.getScreenCTM();
        p = p.matrixTransform(matrix.inverse());
        return 
            x: p.x,
            y: p.y
        

【问题讨论】:

【参考方案1】:

你很亲密。你有几个错误。例如。

copy.onmousedown=copy.onmousemove=onmouseup=Drag;

应该是:

copy.onmousedown=copy.onmousemove=copy.onmouseup=Drag;

drag = t 应该是drag = copy (?)

当我认为您打算将它们附加到“画布”部分时,您还将克隆附加到库存部分。

但也有一些不太明显的错误导致了不可靠性。例如,如果您将 mousemove 和 mouseup 事件附加到库存和克隆形状,那么如果拖得太快,您将不会收到事件。鼠标将移到形状之外,并且事件不会传递给形状。解决方法是将这些事件处理程序移动到根 SVG。

我所做的另一项更改是将克隆的 DOM 中的 xy 位置存储为 _x_y。这比尝试将它们保存在单独的数组中更容易。

无论如何,这是我对你的示例的修改版本,它的工作更加可靠。

// define meta objects
var drag = null;

var canvas = ;
var inventory = ;
	
window.onload = function() 
		
    canvas = document.getElementById("canvas");
	inventory = document.getElementById("inventory");
		
	// attach events listeners
	AttachListeners();


function AttachListeners() 
	var ttt = document.getElementsByClassName('inventory'), i;
	for (i = 0; i < ttt.length; i++) 
        document.getElementsByClassName("inventory")[i].onmousedown=Drag;
	
    document.getElementById("svg").onmousemove=Drag;
	document.getElementById("svg").onmouseup=Drag;


// Drag function that needs to be modified;//
function Drag(e) 
    var t = e.target, id = t.id, et = e.type;  m = MousePos(e);
  
	if (!drag && (et == "mousedown")) 
				
		if (t.className.baseVal=="inventory")  //if its inventory class item, this should get cloned into draggable?
	    	copy = t.cloneNode(true);
			copy.onmousedown = Drag;
            copy.removeAttribute("id");
            copy._x = 0;
            copy._y = 0;
			canvas.appendChild(copy);
			drag = copy;
			dPoint = m;
		 
		else if (t.className.baseVal=="draggable")	 //if its just draggable class - it can be dragged around
			drag = t;
			dPoint = m;
		
	

    // drag the spawned/copied draggable element now
	if (drag && (et == "mousemove")) 
		drag._x += m.x - dPoint.x;
		drag._y += m.y - dPoint.y;
		dPoint = m;
		drag.setAttribute("transform", "translate(" +drag._x+","+drag._y+")");	
	
		
    // stop drag
	if (drag && (et == "mouseup")) 
		drag.className.baseVal="draggable";
		drag = null;
	

         
		

// adjust mouse position to the matrix of SVG & screen size
function MousePos(event) 
		var p = svg.createSVGPoint();
		p.x = event.clientX;
		p.y = event.clientY;
		var matrix = svg.getScreenCTM();
		p = p.matrixTransform(matrix.inverse());
		return 
			x: p.x,
			y: p.y
		
/* SVG styles */
path

	stroke-width: 4;
	stroke: #000;
	stroke-linecap: round;


path.fill

	fill: #3ff;


html, body 
	margin: 0;
	padding: 0;
	border: 0;
	overflow:hidden;
	background-color: #fff;	

body 
	-ms-touch-action: none;

#canvasBackground 
	fill: lightgrey;

#inventoryBackground 
	fill: grey;

.inventory 
  fill: red;

.draggable 
  fill: green;

svg 
    position: fixed; 
	  top:0%; 
	  left:0%; 
	  width:100%; 
	  height:100%;  
<svg id="svg"
  
  
	viewbox="0 0 480 800"
	preserveAspectRatio="xMinYMin meet"
	xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink"
>
	<rect id="canvasBackground"   x="0" y="0"/>
	<rect id="inventoryBackground"   x="0" y="480"/>
  

<g id="inventory">
<path id="curve4" class="inventory" d="M60,530 A35,35 0 1,1 60,531" />
<path id="curve3" class="inventory" d="M150,530 A35,35 0 1,1 150,531" />
<path id="curve2" class="inventory" d="M240,530 A35,35 0 1,1 240,531" />
<path id="curve1" class="inventory" d="M330,530 A35,35 0 1,1 330,531" />
</g>
  
<g id="canvas">
</g>

</svg>

【讨论】:

嘿,保罗,非常感谢;)我一步一步地经历了它,现在我对如何做这些事情有了更好的理解。

以上是关于生成和拖动 SVG 元素 - 方法的主要内容,如果未能解决你的问题,请参考以下文章

jQuery UI 重新生成(僵尸)可拖动元素

在 IE 和 Edge 上拖动 svg 元素的精灵

如何将子元素以外的元素用作拖动手柄?

必备的Canvas接口和动画效果大全

简单实现svg的拖拽和缩放

Vue支持svg元素吗?