产卵&拖动SVG元素 - 方法

时间:2016-10-27 05:08:17

标签: javascript svg clone element draggable

我正在学习Javascript / SVG组合(动画制作交互式SVG)的学习曲线。

我想创建一个代码片段,其中菜单元素(“库存”)可以拖到主屏幕(“画布”),而原始元素将保留在其位置(就像有人会移动它的副本一样)关闭原始元素。)

在这里,我尽可能地制作了代码片段: http://codepen.io/cmer41k/pen/f2b5eea274cdde29b0b2dc8a2424a645

所以我设法做了一些事情,但它的错误:

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

  2. 我无法理解如何让它无限期地工作(这样它可以产生任意数量的可拖动到画布上的圆圈)

  3. 画布中的可拖动元素经常重叠,我无法以不重叠的方式附加侦听器,因此我拖动的元素上的侦听器将“通过”任何其他元素传播;(

  4. 问题基本上是 - 有人可以提出我应该在这个片段中加入的逻辑,这样它就不那么麻烦了。我很确定我在这里遗漏了一些东西;((例如它不应该那么难吗?)

    HTML:

    <body>
    <svg id="svg"
      height="800"
      width="480"
        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" width="480" height="480" x="0" y="0"/>
        <rect id="inventoryBackground" width="480" height="100" 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 个答案:

答案 0 :(得分:3)

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

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

应该是:

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

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

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

但也有一些不那么明显的错误导致了不可靠性。例如,如果将mousemove和mouseup事件附加到清单和克隆形状,那么如果拖得太快,您将无法获得事件。鼠标将超出形状,并且事件不会传递给形状。修复是将这些事件处理程序移动到根SVG。

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

无论如何,这是我的示例的修改版本,它可以更加可靠地运行。

&#13;
&#13;
// 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
		}
}
&#13;
/* 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%;  
}
&#13;
<svg id="svg"
  height="800"
  width="480"
	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" width="480" height="480" x="0" y="0"/>
	<rect id="inventoryBackground" width="480" height="100" 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>
&#13;
&#13;
&#13;