【问题标题】: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 事件存储信息,例如图像。但是,触摸事件无法进行此类操作。

现在我想知道,有没有办法在移动设备上做同样的事情,通过使用触摸事件来模仿拖动事件?

【问题讨论】:

    标签: javascript mobile drag-and-drop touch-event


    【解决方案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 就足够了 - 无需更改代码)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-04-03
          • 2020-12-09
          • 1970-01-01
          • 2015-11-01
          • 1970-01-01
          • 2018-09-15
          • 1970-01-01
          • 2023-03-18
          相关资源
          最近更新 更多