最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - HTML Drag and Drop on touchscreen - Stack Overflow

programmeradmin3浏览0评论

I am working on a Task which involves dragging of images and checking where it is dropping and performing action if it is dropped on right location. While it is working absolutely fine on anything that has a mouse it does not work on a touchscreen. How can achieve this goal on a touchscreen. using Vuejs 2 or vanilla javascript

Drag Item

<v-row v-for="(item, iterator) in Activity.drag_items" :key="item.class" :class="[item.class, item.status]" class="drag-item">
   <v-img 
      draggable
      @dragstart='startDrag($event, item, iterator)'
      :src="require(`@/assets/img/activities/activity_2/${item.item_img}`)"
      contain
      :class="item.status"
   ></v-img>
</v-row>

Drop Item

<a @drop='onDrop($event, Activity)' @dragover.prevent @dragenter.prevent></a>

On Drag Function

startDrag(evt, item, index){
        evt.dataTransfer.dropEffect = 'move';
        evt.dataTransfer.effectAllowed = 'move';
        evt.dataTransfer.setData('item', JSON.stringify(item));
        evt.dataTransfer.setData('index', index);
    }

On Drop Function

onDrop(evt, galaxy_location) {}

I am working on a Task which involves dragging of images and checking where it is dropping and performing action if it is dropped on right location. While it is working absolutely fine on anything that has a mouse it does not work on a touchscreen. How can achieve this goal on a touchscreen. using Vuejs 2 or vanilla javascript

Drag Item

<v-row v-for="(item, iterator) in Activity.drag_items" :key="item.class" :class="[item.class, item.status]" class="drag-item">
   <v-img 
      draggable
      @dragstart='startDrag($event, item, iterator)'
      :src="require(`@/assets/img/activities/activity_2/${item.item_img}`)"
      contain
      :class="item.status"
   ></v-img>
</v-row>

Drop Item

<a @drop='onDrop($event, Activity)' @dragover.prevent @dragenter.prevent></a>

On Drag Function

startDrag(evt, item, index){
        evt.dataTransfer.dropEffect = 'move';
        evt.dataTransfer.effectAllowed = 'move';
        evt.dataTransfer.setData('item', JSON.stringify(item));
        evt.dataTransfer.setData('index', index);
    }

On Drop Function

onDrop(evt, galaxy_location) {}
Share Improve this question asked Jun 16, 2021 at 9:46 Muhammad Usama MalikMuhammad Usama Malik 551 silver badge5 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 5

As for now, there is no dataTransfer object for touch event. One way to do this is to have methods that copy the value or data and mutate it base on the touch event. In my example I have three methods to simulate the drag and drop with touch

On touch start, I stored the reference that is needed into object, this is similar to dataTransfer.setData(), but added work here is to simulate the feeling of drag and drop by removing item on touchstart and duplicate a new element to follow your touch event

  touchstartDrag(e, item, arr) {
  // go through origin array
  arr.forEach((el, i) => {
    if (el == item) {
      // store it as reference
      this.touchDragItem = {
        item: item,
        index: i,
        arr: arr
      }
      // remove item in the array, or you can change opacity
      arr.splice(i, 1)
    }
  })
  let image = document.createElement("img"); // Create a new element
  image.setAttribute("id", "image-float");


  // get the image from the stored reference
  image.src = `https://cdn.quasar.dev/img/avatar${this.touchDragItem.item}.jpg`;
  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';


  document.getElementById('app').appendChild(image);
},

On touchmove, this is purely to simulate the dragging feeling that you get from dnd, get the element created in touchstart and make it follow your touchmove

    touchmoveDrag(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';
  this.touchX = e.touches[0].pageX
  this.touchY = e.touches[0].pageY

},

On touch end where you define your drop function. Because there is no drop event, you have to manually detect your touch according to the dropzone, if it's outside the dropzone, define your logic, if it's within, execute it accordingly as you define dataTransfer.getData()

    touchendDrag(e) {
  // remove the image on touch end
  let image = document.getElementById('image-float')
  image.remove()
  // get the dropzone of top and bottom
  let rect1 = document.getElementById('top').getBoundingClientRect();
  let rect2 = document.getElementById('bottom').getBoundingClientRect()
  // to detect the overlap of mouse into the dropzone, as alternative of mouseover
  var overlapTop = !(rect1.right < this.touchX ||
    rect1.left > this.touchX ||
    rect1.bottom < this.touchY ||
    rect1.top > this.touchY)
  // to detect the overlap of mouse into the dropzone bottom
  var overlapBottom = !(rect2.right < this.touchX ||
    rect2.left > this.touchX ||
    rect2.bottom < this.touchY ||
    rect2.top > this.touchY)
  // get the stored reference
  let ex = this.touchDragItem
  // if on touchend the touch is not inside any dropzone, just restore back to the original array
  if (!overlapTop && !overlapBottom) {
    ex.arr.splice(ex.index, 0, ex.item)
  } else {
    if (overlapTop) {
      if (this.top == ex.arr) {
        ex.arr.splice(ex.index, 0, ex.item)
      }
      if (this.top != ex.arr) {
        this.top.push(ex.item)

      }


    }
    if (overlapBottom) {
      if (this.bottom == ex.arr) {
        ex.arr.splice(ex.index, 0, ex.item)
      }
      if (this.bottom != ex.arr) {
        this.bottom.push(ex.item)

      }
    }
  }
  this.touchDragItem = null

},

Overall this is a naive approach to simulate dnd API for touch event, there are plenty of vue.js drag and drop library for you to go, depends on your use case such as sortable.js . But if you want to implement your own touch drag, this is somewhere you can start off

Here is the full working example

new Vue({
  el: "#app",
  data: {
    touchDragItem: null,
    touchX: null,
    touchY: null,
    top: ['1', '2', '3'],
    bottom: [],
  },
  methods: {
    dragmouse(e, item) {
      e.dataTransfer.dropEffect = 'move'
      e.dataTransfer.effectAllowed = 'move'
      e.dataTransfer.setData('item', item)
    },
    onDrop(e, pos) {
      let arr = pos == 'top' ? 'bottom' : 'top'
      let item = e.dataTransfer.getData('item')
      this[arr].forEach((el, i) => {
        if (el == item) {
          this[arr].splice(i, 1)
          this[pos].push(el)
        }
      })
    },


    touchstartDrag(e, item, arr) {
      // go through origin array
      arr.forEach((el, i) => {
        if (el == item) {
          // store it as reference
          this.touchDragItem = {
            item: item,
            index: i,
            arr: arr
          }
          // remove item in the array, or you can change opacity
          arr.splice(i, 1)
        }
      })
      let image = document.createElement("img"); // Create a new element
      image.setAttribute("id", "image-float");


      // get the image from the stored reference
      image.src = `https://cdn.quasar.dev/img/avatar${this.touchDragItem.item}.jpg`;
      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';


      document.getElementById('app').appendChild(image);
    },


    touchmoveDrag(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';
      this.touchX = e.touches[0].pageX
      this.touchY = e.touches[0].pageY

    },
    touchendDrag(e) {
      // remove the image on touch end
      let image = document.getElementById('image-float')
      image.remove()
      // get the dropzone of top and bottom
      let rect1 = document.getElementById('top').getBoundingClientRect();
      let rect2 = document.getElementById('bottom').getBoundingClientRect()
      // to detect the overlap of mouse into the dropzone, as alternative of mouseover
      var overlapTop = !(rect1.right < this.touchX ||
        rect1.left > this.touchX ||
        rect1.bottom < this.touchY ||
        rect1.top > this.touchY)
      // to detect the overlap of mouse into the dropzone bottom
      var overlapBottom = !(rect2.right < this.touchX ||
        rect2.left > this.touchX ||
        rect2.bottom < this.touchY ||
        rect2.top > this.touchY)
      // get the stored reference
      let ex = this.touchDragItem
      // if on touchend the touch is not inside any dropzone, just restore back to the original array
      if (!overlapTop && !overlapBottom) {
        ex.arr.splice(ex.index, 0, ex.item)
      } else {
        if (overlapTop) {
          if (this.top == ex.arr) {
            ex.arr.splice(ex.index, 0, ex.item)
          }
          if (this.top != ex.arr) {
            this.top.push(ex.item)

          }


        }
        if (overlapBottom) {
          if (this.bottom == ex.arr) {
            ex.arr.splice(ex.index, 0, ex.item)
          }
          if (this.bottom != ex.arr) {
            this.bottom.push(ex.item)

          }
        }
      }
      this.touchDragItem = null

    },
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

button {
  color: #4fc08d;
}

button {
  background: none;
  border: solid 1px;
  border-radius: 2em;
  font: inherit;
  padding: 0.75em 2em;
}

.dropzone {
  display: flex;
  height: fit-content;
  min-width: 50px;
  min-height: 50px;
  background: #2D2D2D;
  margin: 10px;
  padding: 10px;
}

.dropzone>* {
  margin: 0 5px;
}
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div id="top" class="dropzone" @drop="onDrop($event,'top')" @dragenter.prevent @dragover.prevent>
    <img ref="image" draggable @dragstart="dragmouse($event,item)" @touchstart.prevent="touchstartDrag($event,item,top)" @touchmove="touchmoveDrag" @touchend="touchendDrag" :src="`https://cdn.quasar.dev/img/avatar${item}.jpg`" width="100" height="100" v-for="item in top"
      :key="item" />
  </div>


  <div id="bottom" class="dropzone" @drop="onDrop($event,'bottom')" @dragenter.prevent @dragover.prevent>
    <img ref="image" draggable @dragstart="dragmouse($event,item)" @touchstart.prevent="touchstartDrag($event,item,bottom)" @touchmove="touchmoveDrag" @touchend="touchendDrag" :src="`https://cdn.quasar.dev/img/avatar${item}.jpg`" width="100" height="100"
      v-for="item in bottom" :key="item" />
  </div>
</div>

发布评论

评论列表(0)

  1. 暂无评论