我正在为SVG元素创建一个可拖动的函数。它完全按照我想要的那样工作,但我想知道是否有人可以帮助解释我有几个关于函数如何工作的问题。
这个简化的示例显示了我的函数,其中:
橙色线本身是可拖动的。
红色圆圈不可拖动。
蓝色正方形和绿色三角形可拖动在一起。
"use strict"
/* Make an SVG element dragable using the translate transfrom. */
const makeDraggable = (el) => {
/* Pointer SVG coordinates */
const getPointerPosition = (e) => {
if (e.touches) e = e.touches[0];
return new DOMPoint(e.clientX, e.clientY).matrixTransform(svg.getScreenCTM().inverse());
}
/* Mouse down / touch start event */
const dragStart = (e) => {
/* Add event listeners. */
root.addEventListener('mousemove', dragMove);
root.addEventListener('touchmove', dragMove);
root.addEventListener('mouseup', dragEnd);
root.addEventListener('mouseleave', dragEnd);
root.addEventListener('touchend', dragEnd);
root.addEventListener('touchleave', dragEnd);
root.addEventListener('touchcancel', dragEnd);
/* Recursive check to find first parent group. If found, apply transform to group. */
let g = el;
while (g && g.tagName !== 'g') g = g.parentNode;
if (g) el = g;
/* Check if element already has a translate as it's first transform. If not, add it. */
const transforms = el.transform.baseVal;
if (transforms.length === 0 || transforms.getItem(0).type !== SVGTransform.SVG_TRANSFORM_TRANSLATE)
translate = transforms.insertItemBefore(svg.createSVGTransform(), 0);
else translate = transforms.getItem(0);
/* Get pointer position during initial down/touch and offset the difference to the element translate origin. */
offset = getPointerPosition(e);
offset.x -= translate.matrix.e;
offset.y -= translate.matrix.f;
}
/* Mouse move / touch move event */
const dragMove = (e) => {
/* Get pointer position of move, apply difference from initial pointer position to translate transform. */
const point = getPointerPosition(e);
translate.setTranslate(point.x -= offset.x, point.y -= offset.y);
}
/* Mouse up / leave / touch end / leave / cancel event */
const dragEnd = () => {
/* Remove inactive event listeners. */
root.removeEventListener('mousemove', dragMove);
root.removeEventListener('touchmove', dragMove);
root.removeEventListener('mouseup', dragEnd);
root.removeEventListener('mouseleave', dragEnd);
root.removeEventListener('touchend', dragEnd);
root.removeEventListener('touchleave', dragEnd);
root.removeEventListener('touchcancel', dragEnd);
}
/* Attach event listeners to dragable element. */
el.addEventListener('mousedown', dragStart);
el.addEventListener('touchstart', dragStart);
/* Set variables. */
let svg = el;
while (svg && svg.tagName !== 'svg') svg = svg.parentNode;
const doc = svg.ownerDocument;
const root = doc.documentElement || doc.body || svg;
let translate, offset;
}
const loadSVG = () => {
document.querySelectorAll(".draggable").forEach(el => makeDraggable(el))
}
* {
margin: 0;
}
svg {
background-color: lightblue;
width: 200px;
height: 200px;
}
.draggable {
cursor: move;
}
.notdraggable {
cursor: not-allowed;
}
Drag Test
我的问题是:
当SVG元素加载时,调用loadSVG函数,该函数对每个可拖动元素执行makeDraggable函数。该函数添加mousedown & touchstart事件侦听器,并将它们分配给每个可拖动元素的dragStart函数。
后来,当所有的加载函数都完成后,在其中一个可拖动元素上发生了mousedown/touchstart事件,它去调用dragStart函数,它如何找到dragStart函数,因为这个函数是在makeDraggable函数中找到的?
类似地,makeDraggble函数还创建变量translate & offset,用于存储dragStart函数(在mousedown/touchstart事件上调用)和dragMove (在mousemove/touchmove事件上调用)之间使用的信息值。但我只看到只有在调用makeDraggable函数时才会创建这些变量,只有在加载SVG时才会调用loadSVG函数。
所以,在运行完所有的load函数,并触发mousedown/touchstart & mousemove/touchmove事件之后,它如何仍然能找到这些变量呢?
我看到了另一个例子。el变量来自makeDraggable函数的一个参数,同样,该函数仅在加载期间调用。但是el变量是在dragStart (mousedown/touchstart)函数期间使用的,该函数是在不同的时间调用的。那么dragStart函数如何仍然使用这个变量呢?它不是一个全局变量,它的用法根据所单击的元素而变化。
我怀疑所有这些问题的答案可能是相同的?
发布于 2021-02-27 03:02:30
向对象添加事件侦听器时,所添加的函数(以及在事件上执行)具有一定的封闭作用域。该作用域包括变量和函数。
而function
和箭头函数的不同之处在于它们的封闭范围,我认为对您的问题的答案是,dragStart
,它确实是在makeDraggable
被“绑定”到el
通过事件侦听器。
el
不仅仅是像这样的值1
或者true
而是对对象的引用。在此之后,该对象将继续存在makeDraggable
被处死。
本例中的浏览器“存储”了对dragStart
它同样包含对其他函数和对象的引用。当您注销事件侦听器时,浏览器只会“忘记”这些事件。
这里有比我所描述的更多的细节。
如果您想了解更多信息,可以查看
https://stackoverflow.com/questions/66390783
复制相似问题