HTML5拖放:手动实现整个序列



我有一个复杂的UI组件,带有自定义的Drag&删除几百行的行为和自定义事件代码,基本上通过常规MouseEvents工作。

我需要添加额外的功能,以便用户可以拖动&将项目从组件内部拖放到组件外部。";外部";通过常规的HTML5Drag&Drop事件,意味着有一个元素已经监听了Drop事件等。现在,我只需要触发整个Drag&Drop chain,这样Drop zone元素就会注意到该事件,并且在拖动操作期间,我会得到常规外观的拖动图像。

简单地将我的可拖动项设置为draggable="true"是不起作用的,因为dragstart事件没有抛出,它被event.preventDefault()组件内的自定义事件代码阻止了。因此,我的想法是,每当用户想将项目拖动到组件边界之外时,手动触发dragstart事件。

我的当前状态是,一旦我的onMoveStart()意识到用户想要将项目拖到组件之外,它就会触发dragstart事件。onMove()在移动鼠标时触发常规的drag事件,onMoveEnd()触发相应的dragend/drop事件。这里可以看到一个有点相似且非常简单的版本:Fiddle。

问题:当将项目拖动到组件外部时,dragstart会被正确触发,当常规鼠标移动时,拖动事件会被正确地触发,拖动事件也会起作用。但是,dropzone元素从不接收drop事件。此外,在拖动操作期间没有拖动图像,这使得用户看起来不起作用。我手动设置的数据传输总是将effectAlloweddropOperation设置为none,即使我手动将它们设置为适当的值。

我该怎么做?如何实现Drag&手动放弃行为?如何手动设置DataTransfer上的effectAlloweddropEffect属性,以便在不总是将它们覆盖到none的情况下获得适当的拖动效果?

根据您提供的代码,我制作了一些围绕它构建的东西。根据您的帖子和我的理解,我相信您正在尝试解决的2个或3个问题

我从拖动图像开始。所以我只是克隆我们希望它显示为拖动的元素,用适当的属性设置它

function draggingImage(el) {
let image = draggable.cloneNode(true);
image.setAttribute("id", "drag-image");
image.style.width = el.offsetWidth + 'px'
image.style.height = el.offsetHeight + 'px'
image.style.position = 'fixed'
image.style.left = el.getBoundingClientRect().left + 'px';
image.style.top = el.getBoundingClientRect().top + 'px';
image.style.background = getComputedStyle(draggable).backgroundColor
image.style.opacity = .5
let div = document.body.appendChild(image);
}

就目前而言,拖动图像是静态的,正如预期的那样,我们希望它随着鼠标的移动而移动,从而产生拖动效果

let image = document.getElementById('drag-image')
image.style.left = image.getBoundingClientRect().left + e.movementX + 'px'
image.style.top = image.getBoundingClientRect().top + e.movementY + 'px'

当我们的鼠标移动为wasdragging=true时,这是检测光标下面是什么的好时机,我们是在下降区内还是在外

function getDropzone(e, left, top, id) {
// let rect1 = document.getElementById(id).getBoundingClientRect();
let rect1 = drop.getBoundingClientRect()
// to detect the overlap of mouse into the dropzone, as alternative of mouseover
var overlap = !(rect1.right < left ||
rect1.left > left ||
rect1.bottom < top ||
rect1.top > top)
return overlap
}

我们可以在onDrag函数中添加上述函数。getDropzone函数返回true或false,这些都是必需的。从那里我们可以相应地执行我们的逻辑或样式。

我还添加了称为data的全局变量,然后添加了一个与dataTransfer概念几乎类似的函数。

let data = {}
function manualSetData(variable, v) {
data[variable] = v
}

有了这个,我们就可以在ondragend中获得我们用这个函数设置的数据

let getData = data[variable]

所以这是从头开始的DnD,简而言之就是

const draggable = document.querySelector("#draggable");
const drop = document.querySelector("#drop");
const condition = true;
let wasDragging = false;
let allowedDrop = false
let data = {}
draggable.addEventListener("mousedown", onMoveStart)
document.addEventListener("mousemove", onMove)
document.addEventListener("mouseup", onMoveEnd)
function onMoveStart(e) {
event.preventDefault();
event.stopPropagation();
let el = e.target
// Other logic and custom event handling ...
if (condition) {
onDragStart(el);
}
}
function onMove(e) {
// Other logic ...
if (wasDragging) {
onDrag(e);
}
}
function onMoveEnd(event) {
// Other logic ...
if (wasDragging) {
onDragEnd(event.target);
}
}
function onDragStart(el) {
draggingImage(el)
wasDragging = true;
manualSetData('helloMom', el)
}
function draggingImage(el) {
let image = draggable.cloneNode(true);
image.setAttribute("id", "drag-image");
image.style.width = el.offsetWidth + 'px'
image.style.height = el.offsetHeight + 'px'
image.style.position = 'fixed'
image.style.left = el.getBoundingClientRect().left + 'px';
image.style.top = el.getBoundingClientRect().top + 'px';
image.style.background = getComputedStyle(draggable).backgroundColor
image.style.opacity = .5
let div = document.body.appendChild(image);
}
function manualSetData(variable, v) {
data[variable] = v
}
function onDrag(e) {
let image = document.getElementById('drag-image')
image.style.left = image.getBoundingClientRect().left + e.movementX + 'px'
image.style.top = image.getBoundingClientRect().top + e.movementY + 'px'
let left = e.pageX;
let top = e.pageY;
let overlap = getDropzone(e, left, top)
drop.style.backgroundColor = allowedDrop ? 'green' : 'blue'
if (overlap) {
document.body.style.cursor = 'copy';
allowedDrop = true
} else {
document.body.style.cursor = 'no-drop';
allowedDrop = false
}
}
function getDropzone(e, left, top, id) {
// let rect1 = document.getElementById(id).getBoundingClientRect();
let rect1 = drop.getBoundingClientRect()
// to detect the overlap of mouse into the dropzone, as alternative of mouseover
var overlap = !(rect1.right < left ||
rect1.left > left ||
rect1.bottom < top ||
rect1.top > top)
return overlap
}
function onDragEnd(element) {
let image = document.getElementById('drag-image')
image.remove()
document.body.style.cursor = 'default';
if (allowedDrop) {
let getData = data['helloMom']
drop.appendChild(getData)
}

wasDragging = false;
}
#draggable {
background-color: red;
}
#drop {
background-color: blue;
}
<div id="draggable" draggable="true">
fancy content
</div>
<div id="drop">
drop area
</div>

最新更新