vue实现移动端touch拖拽排序

目录

  • 功能介绍:
  • 大致需求:
  • 整体思路:
  • 简单效果展示:
  • 具体实现:
    • 一、display:flex+v-for布局:
    • 二、touch事件绑定:
    • 三、卡片移动:
    • 四、获取手指所在位置:
    • 五、操作数组(删除或插入元素):
    • 六、手指离开屏幕:
    • 七、备注:
    • 八、完整代码:
本文实例为大家分享了vue实现移动端touch拖拽排序的具体代码,供大家参考,具体内容如下

功能介绍: 在移动端开发中,希望实现类似支付宝应用管理页面的可拖拽排序交互。

大致需求: 1、卡片按照一定顺序排序,超出横向范围换行显示;
2、手指长按卡片,可进行拖拽控制,卡片追随手指移动;
3、卡片移动到相应位置,该位置上的卡片向后或向前更换位置,当前位置空出;
4、松开手指,卡片可回到原位置或新位置进行展示;

整体思路: 1、卡片实行flex弹性布局,通过数组的遍历可自动显示在相应位置;
2、手指长按可使用定时器来判断,若手指松开,则关闭定时器,等待下次操作再启用;
3、跟随手指移动的卡片可使用absolute定位控制,同时根据手指位置判断当前所在位置;
4、位置发生改变时,控制数组添加或删除相应元素,从而实现换位效果;

简单效果展示: vue实现移动端touch拖拽排序
文章图片


具体实现:
一、display:flex+v-for布局:
使用弹性布局实现
  • {{item.name}}

data() {return {list: [{ name: '1' }, // 卡片内容{ name: '2' },{ name: '3' }]}},

ul {width: 100%; height: 100%; display: flex; // 弹性布局flex-wrap: wrap; overflow: hidden; // 超出部分隐藏,目的阻止横向滚动.libox {width: 25%; // 这里以4列为例height: 70px; >div {background-color:#eee; width: calc(100% - 10px); height: 36px; border-radius: 18px; }}}


二、touch事件绑定:
应用到touchstart,touchmove,touchend事件,使用定时器实现长按效果:
{{item.name}}

data() {return {timeOutEvent: 0}; },methods: {// 手指触摸事件touchstart(ev, item) {// 定时器控制长按时间,超过500毫秒开始进行拖拽this.timeOutEvent = setTimeout(() => {this.longClick = 1;}, 500); },// 手指在屏幕上移动touchMove(ev) {// 未达到500毫秒就移动则不触发长按,清空定时器clearTimeout(this.timeOutEvent); },// 手指离开屏幕touchEnd() {clearTimeout(this.timeOutEvent); }}


三、卡片移动:
在ul中增加一个独立的不在循环中的li标签,改为absolute定位,通过动态修改li标签top、left属性实现跟随手指移动效果。
  • {{selectItem.name}}

ul {position: relative; // 此li标签的样式与循环li标签内的div样式保持一致// 背景色加深,代表被手指选中.selectBox {position: absolute; width: calc(25% - 10px); height: 36px; border-radius: 18px; background-color:#6981c8; color:white; }}

当卡片被选中,将卡片内容赋值给全局变量,判断卡片显示隐藏(v-show判断,隐藏但占位),实现选中元素位置空出效果:
手指位置通过touchmove获取:
{{item.name}}

touchstart(ev, item) {this.timeOutEvent = setTimeout(() => {this.longClick = 1; this.selectItem = item; // 将卡片内容赋值给全局变量const selectDom = ev.target; // li元素// 元素初始位置this.oldNodePos = {x: selectDom.offsetLeft,y: selectDom.offsetTop}; // 鼠标原始位置this.oldMousePos = {x: ev.touches[0].pageX,y: ev.touches[0].pageY}; const lefts = this.oldMousePos.x - this.oldNodePos.x; // x轴偏移量const tops = this.oldMousePos.y - this.oldNodePos.y; // y轴偏移量const { pageX, pageY } = ev.touches[0]; // 手指位置this.$refs.selectBox.style.left = `${pageX - lefts}px`; this.$refs.selectBox.style.top = `${pageY - tops}px`; }, 500); },touchMove(ev) {clearTimeout(this.timeOutEvent); // this.longClick === 1判断是否长按if (this.longClick === 1) {const selectDom = ev.target.parentNode; // li元素const lefts = this.oldMousePos.x - this.oldNodePos.x; // x轴偏移量const tops = this.oldMousePos.y - this.oldNodePos.y; // y轴偏移量const { pageX, pageY } = ev.touches[0]; // 手指位置this.$refs.selectBox.style.left = `${pageX - lefts}px`; this.$refs.selectBox.style.top = `${pageY - tops}px`; }}


四、获取手指所在位置:
cardIndex(selDom, moveleft, movetop) {const liWid = selDom.clientWidth; // li宽度const liHei = selDom.clientHeight; // li高度const newWidNum = Math.ceil((moveleft / liWid)); // 手指所在列const newHeiNum = Math.ceil((movetop / liHei)); // 手指所在行const newPosNum = (newHeiNum - 1) * 4 + newWidNum; // 手指所在位置// 判断是否是新位置并且没有超出列表数量范围if (this.oldIndex !== newPosNum && newPosNum <= this.list.length) {// 将新的位置赋值给全局变量oldIndexthis.oldIndex = newPosNum; }}


五、操作数组(删除或插入元素):
监听oldIndex的值,若发生改变则执行操作数组函数
watch: {oldIndex(newVal) {const oldIndex = this.list.indexOf(this.selectItem); this.list.splice(oldIndex, 1); this.list.splice(newVal - 1, 0, this.selectItem); }},


六、手指离开屏幕:
手指离开屏幕,清空选中的元素selectItem,跟随手指移动的卡片(li.selectBox)自动隐藏,在循环中隐藏的卡片(li)则会显示,实现换位效果。
touchEnd() {clearTimeout(this.timeOutEvent); this.selectItem = {}; }


七、备注:
上面的代码是基于div容器内只有文字没有其他dom元素实现,后发现若div中存在dom元素例如svg,则【$event】选中的值会变成其子元素,且拖拽排序出现问题,希望知道原因的小伙伴可以评论或私信告诉我一下,非常感谢。
粗暴的解决方式:
div容器增加after蒙版,可设置为透明色:
div position: relative; &::after {content: ''; width: 100%; height: 100%; background: rgba(255, 177, 177, 0.3); // 背景色position: absolute; top: 0; left: 0; }}


八、完整代码:
@mixin myFlexCenter{display: flex; justify-content: center; align-items: center; }ul {width: 100%; height: 100%; display: flex; flex-wrap: wrap; position: relative; overflow: hidden; .libox {width: 25%; height: 100px; border-right: 1px dashed #cccccc; border-bottom: 1px dashed #cccccc; box-sizing: border-box; @include myFlexCenter; >div {width: calc(100% - 10px); height: 75px; border-radius: 18px; @include myFlexCenter; position: relative; &::after {content: ''; width: 100%; height: 100%; background: rgba(255, 177, 177, 0.3); position: absolute; top: 0; left: 0; }>svg {width: 75px; height: 75px; }}}.selectBox{position: absolute; width: calc(25% - 10px); height: 75px; border-radius: 18px; >svg {width: 75px; height: 75px; }background-color: rgba(0, 0, 0, 0.1); color:white; @include myFlexCenter; -moz-user-select:none; /*火狐*/-webkit-user-select:none; /*webkit浏览器*/-ms-user-select:none; /*IE10*/-khtml-user-select:none; /*早期浏览器*/user-select:none; }}

【vue实现移动端touch拖拽排序】以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    推荐阅读