生成和拖动 SVG 元素 - 方法

spawn & drag of SVG elements - approach

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

我想创建一个代码片段,其中可以将菜单元素 ("inventory") 拖到主屏幕 ("canvas") 而原始元素将保留在其位置(就像一个会将其副本从原始元素中移走)。

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

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

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

  2. 我不明白如何让它无限期地工作(以便它可以生成任意数量的可拖动到 canvas 的圆圈)

  3. canvas 中的可拖动元素经常重叠,我无法以不重叠的方式附加监听器,因此我拖动的元素上的监听器会传播 "through" 那里有任何其他元素;(

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

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
        }
}

你很接近。你有几个错误。例如

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

应该是:

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

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

你还在将克隆附加到库存部分,而我认为你打算将它们附加到 "canvas" 部分。

但也有一些不太明显的错误导致了不可靠性。例如,如果您将 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"
  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>