使用移动设备的触摸事件模拟拖放事件
Posted
技术标签:
【中文标题】使用移动设备的触摸事件模拟拖放事件【英文标题】:Imitating Drag and Drop Events with Touch Events for Mobile Devices 【发布时间】:2019-05-01 00:20:14 【问题描述】:前段时间,我在移动设备上的网络浏览器中进行拖放操作。默认的 javascript 事件在移动设备上不起作用。您只能使用触摸事件。
就我而言,我需要通过拖放来交换两个图像和 ID。举个例子:
div
display:inline-block;
border:1px solid #0b79d0;
div, img
width:120px;
height:120px;
<div id="1" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="a" draggable="true" ondragstart="dragStart(event)" src="https://static.webshopapp.com/shops/073933/files/156288269/345x345x1/artibalta-white-tiger.jpg"/>
</div>
<div id="2" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="b" draggable="true" ondragstart="dragStart(event)" src="https://yt3.ggpht.com/a-/AN66SAyfsmao4f1EEOqkBP2PgpSUcabPJXLZ1sLEnA=s288-mo-c-c0xffffffff-rj-k-no"/>
</div>
<div id="3" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="c" draggable="true" ondragstart="dragStart(event)" src="https://kinderbilder.download/wp-content/uploads/2018/10/animals-for-dolphin-drawings-pencil-drawings-pinterest-verwandt-mit-delfine-zeichnen-100x100.jpg"/>
</div>
<script>
function allowDrop(ev)
ev.preventDefault();
function dragEnter(ev)
var element = document.getElementById(ev.target.id);
element.style.border = "dotted";
element.style.borderColor = "#0b79d0";
function dragLeave(ev)
var element = document.getElementById(ev.target.id);
element.style.border = "1px solid #0b79d0";
function dragStart(ev)
ev.dataTransfer.setData("src", ev.target.id);
var number = ev.target.id.replace ( /[^\d.]/g, '' );
ev.dataTransfer.setData("text/plain", number);
function drop(ev)
ev.preventDefault();
var src = document.getElementById(ev.dataTransfer.getData("src"));
var srcParent = src.parentNode;
var tgt = ev.currentTarget.firstElementChild;
ev.currentTarget.replaceChild(src, tgt);
srcParent.appendChild(tgt);
var number1 = srcParent.id.replace(/[^\d.]/g, '');
var number2 = ev.currentTarget.id.replace(/[^\d.]/g, '');
var element = document.getElementById(ev.target.id);
element.style.border = "solid 1px #0b79d0";
var number = ev.target.id.replace(/[^\d.]/g, '');
</script>
所以dragStart 事件存储信息,例如图像。但是,触摸事件无法进行此类操作。
现在我想知道,有没有办法在移动设备上做同样的事情,通过使用触摸事件来模仿拖动事件?
【问题讨论】:
【参考方案1】:我强烈建议您在移动设备中使用库来执行此操作。 我不得不做相对相同的行为,我使用了interact.js,它非常少并且可以移动。
如果您只想为您的应用添加触摸手势,请查看Hammer.js。它是使用触摸事件的主要库。
如果您使用 jQuery,请查看 topic。
我希望这会有所帮助。
【讨论】:
【参考方案2】:对于新来者,目前还没有用于触摸事件的dataTransfer(还没有?)。您可以做与DnD 相同的功能,但需要做更多的工作。 DnD 只是简化了数据传输过程并处理了检测 Dragenter 之类的一些事情,但是您可以使用触摸进行相同的操作,只是您必须自己完成所有 Dragenter 检测工作。
在触摸开始时,我将拖动元素的引用存储到变量中,这类似于 dataTransfer.setData(),但这里添加的工作是通过复制一个新元素来跟随你的触摸来模拟拖放的感觉事件
function dragTouchstart(e)
//global variable that store the touch/drag element
imgId = e.target
let image = document.createElement("img"); // Create a new element
image.setAttribute("id", "image-float");
// get the image from the stored reference
image.src =imgId.src
image.width = 100
image.height = 100
// position the image to the touch, can be improve to detect the position of touch inside the image
let left = e.touches[0].pageX;
let top = e.touches[0].pageY;
image.style.position = 'absolute'
image.style.left = left + 'px';
image.style.top = top + 'px';
image.style.opacity = 0.5;
document.body.appendChild(image);
在touchmove上,这纯粹是为了模拟你从dnd得到的拖拽感,获取touchstart中创建的元素,让它跟随你的touchmove。但我还添加了 touchenter 函数来检测 dragenter
function dragTouchmove(e)
// on touch move or dragging, we get the newly created image element
let image = document.getElementById('image-float')
// this will give us the dragging feeling of the element while actually it's a different element
let left = e.touches[0].pageX;
let top = e.touches[0].pageY;
image.style.position = 'absolute'
image.style.left = left + 'px';
image.style.top = top + 'px';
let touchX = e.touches[0].pageX
let touchY = e.touches[0].pageY
//apply touch enter fucntion inside touch move
dragTouchenter(e,touchX,touchY)
我的 touchenter 函数
function dragTouchenter(e,touchX,touchY)
let one =document.getElementById('1')
let two =document.getElementById('2')
let three =document.getElementById('3')
let id1 = one.getBoundingClientRect();
let id2 = two.getBoundingClientRect();
let id3 = three.getBoundingClientRect();
// to detect the overlap of touchmove with dropzone
var overlap1 = !(id1.right < touchX ||
id1.left > touchX ||
id1.bottom < touchY ||
id1.top > touchY)
var overlap2 = !(id2.right < touchX ||
id2.left > touchX ||
id2.bottom < touchY ||
id2.top > touchY)
var overlap3 = !(id3.right < touchX ||
id3.left > touchX ||
id3.bottom < touchY ||
id3.top > touchY)
//detect touchenter then apply style, if false, equal to touchleave
//could write a better function to take these elements in array and apply function accordingly
//but as for now I just write like this because faster by copy+paste
//get dropZoneId too
if(overlap1)
one.style.border = "dotted";
one.style.borderColor = "#0b79d0";
dropZoneId =one
else
one.style.border = "1px solid #0b79d0";
if(overlap2)
two.style.border = "dotted";
two.style.borderColor = "#0b79d0";
dropZoneId =two
else
two.style.border = "1px solid #0b79d0";
if(overlap3)
three.style.border = "dotted";
three.style.borderColor = "#0b79d0";
dropZoneId =three
else
three.style.border = "1px solid #0b79d0";
if(!overlap1 && !overlap2 && !overlap3)
dropZoneId = ''
/* console.log(dropZoneId.id) */
最后,ontouchend 将完成您将使用 dataTransfer.getData() 执行的所有逻辑
function dragTouchend(e)
//remove dragged image duplicate
let image = document.getElementById('image-float')
image.remove()
dropZoneId.style.border = "1px solid #0b79d0";
//if outside any dropzone, just do nothing
if(dropZoneId == '')
dropZoneId = ''
imgId = ''
else
// if inside dropzone, swap the image
let toSwap = dropZoneId.children[0]
let originDropzone= imgId.parentElement
originDropzone.appendChild(toSwap)
dropZoneId.appendChild(imgId)
dropZoneId = ''
imgId = ''
下面是基于 OP 的完整工作示例,更改了图像,因为它似乎已损坏
div
display: inline-block;
border: 1px solid #0b79d0;
div,
img
width: 120px;
height: 120px;
<div id="1" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="a" draggable="true" ondragstart="dragStart(event)" src="https://static.webshopapp.com/shops/073933/files/156288269/345x345x1/artibalta-white-tiger.jpg" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)"
/>
</div>
<div id="2" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="b" draggable="true" ondragstart="dragStart(event)" src="https://yt3.ggpht.com/a-/AN66SAyfsmao4f1EEOqkBP2PgpSUcabPJXLZ1sLEnA=s288-mo-c-c0xffffffff-rj-k-no" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)"
/>
</div>
<div id="3" ondragover="allowDrop(event)" ondrop="drop(event)" ondragenter="dragEnter(event)" ondragleave="dragLeave(event)" draggable="false">
<img id="c" draggable="true" ondragstart="dragStart(event)" src="https://cdn.quasar.dev/img/avatar1.jpg" ontouchstart="dragTouchstart(event)" ontouchmove="dragTouchmove(event)" ontouchend="dragTouchend(event)" />
</div>
<script>
var imgId = 'test'
var dropZoneId = ''
function allowDrop(ev)
ev.preventDefault();
function dragTouchstart(e)
imgId = e.target
let image = document.createElement("img"); // Create a new element
image.setAttribute("id", "image-float");
// get the image from the stored reference
image.src = imgId.src
image.width = 100
image.height = 100
// position the image to the touch, can be improve to detect the position of touch inside the image
let left = e.touches[0].pageX;
let top = e.touches[0].pageY;
image.style.position = 'absolute'
image.style.left = left + 'px';
image.style.top = top + 'px';
image.style.opacity = 0.5;
document.body.appendChild(image);
function dragTouchmove(e)
// on touch move or dragging, we get the newly created image element
let image = document.getElementById('image-float')
// this will give us the dragging feeling of the element while actually it's a different element
let left = e.touches[0].pageX;
let top = e.touches[0].pageY;
image.style.position = 'absolute'
image.style.left = left + 'px';
image.style.top = top + 'px';
let touchX = e.touches[0].pageX
let touchY = e.touches[0].pageY
//apply touch enter fucntion inside touch move
dragTouchenter(e, touchX, touchY)
function dragTouchenter(e, touchX, touchY)
let one = document.getElementById('1')
let two = document.getElementById('2')
let three = document.getElementById('3')
let id1 = one.getBoundingClientRect();
let id2 = two.getBoundingClientRect();
let id3 = three.getBoundingClientRect();
// to detect the overlap of touchmove with dropzone
var overlap1 = !(id1.right < touchX ||
id1.left > touchX ||
id1.bottom < touchY ||
id1.top > touchY)
var overlap2 = !(id2.right < touchX ||
id2.left > touchX ||
id2.bottom < touchY ||
id2.top > touchY)
var overlap3 = !(id3.right < touchX ||
id3.left > touchX ||
id3.bottom < touchY ||
id3.top > touchY)
//detect touchenter then apply style, if false, equal to touchleave
//could write a better function to take these elements in array and apply function accordingly
//but as for now I just write like this because faster by copy+paste
//get dropZoneId too
if (overlap1)
one.style.border = "dotted";
one.style.borderColor = "#0b79d0";
dropZoneId = one
else
one.style.border = "1px solid #0b79d0";
if (overlap2)
two.style.border = "dotted";
two.style.borderColor = "#0b79d0";
dropZoneId = two
else
two.style.border = "1px solid #0b79d0";
if (overlap3)
three.style.border = "dotted";
three.style.borderColor = "#0b79d0";
dropZoneId = three
else
three.style.border = "1px solid #0b79d0";
if (!overlap1 && !overlap2 && !overlap3)
dropZoneId = ''
/* console.log(dropZoneId.id) */
function dragTouchend(e)
//remove dragged image duplicate
let image = document.getElementById('image-float')
image.remove()
dropZoneId.style.border = "1px solid #0b79d0";
//if outside any dropzone, just do nothing
if (dropZoneId == '')
dropZoneId = ''
imgId = ''
else
// if inside dropzone, swap the image
let toSwap = dropZoneId.children[0]
let originDropzone = imgId.parentElement
originDropzone.appendChild(toSwap)
dropZoneId.appendChild(imgId)
dropZoneId = ''
imgId = ''
function dragEnter(ev)
var element = document.getElementById(ev.target.id);
element.style.border = "dotted";
element.style.borderColor = "#0b79d0";
function dragLeave(ev)
var element = document.getElementById(ev.target.id);
element.style.border = "1px solid #0b79d0";
function dragStart(ev)
ev.dataTransfer.setData("src", ev.target.id);
var number = ev.target.id.replace(/[^\d.]/g, '');
ev.dataTransfer.setData("text/plain", number);
function drop(ev)
ev.preventDefault();
var src = document.getElementById(ev.dataTransfer.getData("src"));
var srcParent = src.parentNode;
var tgt = ev.currentTarget.firstElementChild;
ev.currentTarget.replaceChild(src, tgt);
srcParent.appendChild(tgt);
var number1 = srcParent.id.replace(/[^\d.]/g, '');
var number2 = ev.currentTarget.id.replace(/[^\d.]/g, '');
var element = document.getElementById(ev.target.id);
element.style.border = "solid 1px #0b79d0";
var number = ev.target.id.replace(/[^\d.]/g, '');
</script>
【讨论】:
ios Safari 中的越野车 @chovy 我可以知道是什么问题吗? 在 ios 上它只是到处闪烁。 @chovy 这很奇怪.. 是否一致?我在 safari ios(SE) 上测试过,iPadOS 无法重现。 welp,也许这只是野生动物园的事情? 只需使用您的内联演示。【参考方案3】:出于教育目的,您正在做的事情很好。
但对于开发而言,最好不要重新发明***并能够使用工具,随着时间的推移可以帮助您。
所以我向你推荐这个Drag & Drop 库。易于使用、有趣、轻便且维护良好。
【讨论】:
【参考方案4】:前段时间解决它的方法(如果时间是主要因素)是实施触摸打孔 - http://touchpunch.furf.com/
它是 jquery-ui 插件,将触摸事件连接到鼠标事件(因此通常导入 lib 就足够了 - 无需更改代码)
【讨论】:
以上是关于使用移动设备的触摸事件模拟拖放事件的主要内容,如果未能解决你的问题,请参考以下文章