javascript - 触摸屏上的 HTML 拖放
问题描述
我正在处理一项任务,该任务涉及拖动图像并检查它的放置位置并在将其放置在正确位置时执行操作。虽然它在任何有鼠标的东西上都可以正常工作,但它不能在触摸屏上工作。如何在触摸屏上实现这一目标。使用 Vuejs 2 或 vanilla javascript
拖动项目
<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>
掉落物品
<a @drop='onDrop($event, Activity)' @dragover.prevent @dragenter.prevent></a>
拖拽功能
startDrag(evt, item, index){
evt.dataTransfer.dropEffect = 'move';
evt.dataTransfer.effectAllowed = 'move';
evt.dataTransfer.setData('item', JSON.stringify(item));
evt.dataTransfer.setData('index', index);
}
落下功能
onDrop(evt, galaxy_location) {}
解决方案
就目前而言,触摸事件还没有dataTransfer对象。一种方法是使用复制值或数据并根据触摸事件对其进行变异的方法。在我的示例中,我有三种方法来模拟触摸拖放
在触摸启动时,我将所需的引用存储到对象中,这类似于 dataTransfer.setData(),但这里添加的工作是通过在 touchstart 上删除项目并复制一个新元素来模拟拖放的感觉触摸事件
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);
},
在 touchmove 上,这纯粹是为了模拟你从 dnd 得到的拖动感觉,获取在 touchstart 中创建的元素并使其跟随你的 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
},
在您定义放置功能的触摸端。因为没有 drop 事件,你必须根据 dropzone 手动检测你的触摸,如果它在 dropzone 之外,定义你的逻辑,如果它在里面,按照你定义 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
},
总的来说,这是一种模拟触摸事件的 dnd API 的幼稚方法,有很多 vue.js 拖放库供您使用,具体取决于您的用例,例如sortable.js。但是如果你想实现自己的触摸拖动,这是你可以开始的地方
这是完整的工作示例
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.com/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>
推荐阅读
- python - 追加新的不完整行数据后自动填充熊猫数据框列
- microservices - 如何在分布式事务期间锁定对象
- php - 将字符串转换为多维数组
- javascript - 使用反应测试库时尝试解构不可迭代的实例无效
- java - Android Studio:删除数据库中的项目时,如何更新回收站视图中的文本视图?
- python - 随机选择包含元组的列表元素?
- python - Python pandas 在多列上执行相同的聚合
- javascript - Discord.js 机器人“guildMember.addRole 不是函数”(已修复)
- angular-material - mat-flat-button 颜色一旦被点击就会改变,只有在模糊事件之后才会恢复正常颜色
- android - 我想在android中将dd格式的坐标更改为dmm方式