生成和拖动 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 中的 x
和 y
位置存储为 _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 元素 - 方法的主要内容,如果未能解决你的问题,请参考以下文章