返回顶部
首页 > 资讯 > 前端开发 > VUE >vue实现移动端touch拖拽排序
  • 453
分享到

vue实现移动端touch拖拽排序

2024-04-02 19:04:59 453人浏览 八月长安
摘要

目录功能介绍:大致需求:整体思路:简单效果展示:具体实现:一、display:flex+v-for布局:二、touch事件绑定:三、卡片移动:四、获取手指所在位置:五、操作数组(删除

目录
  • 功能介绍:
  • 大致需求:
  • 整体思路:
  • 简单效果展示:
  • 具体实现:
    • 一、display:flex+v-for布局:
    • 二、touch事件绑定:
    • 三、卡片移动:
    • 四、获取手指所在位置:
    • 五、操作数组(删除或插入元素):
    • 六、手指离开屏幕:
    • 七、备注:
    • 八、完整代码:

本文实例为大家分享了Vue实现移动端touch拖拽排序的具体代码,供大家参考,具体内容如下

功能介绍:

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

大致需求:

1、卡片按照一定顺序排序,超出横向范围换行显示;

2、手指长按卡片,可进行拖拽控制,卡片追随手指移动;

3、卡片移动到相应位置,该位置上的卡片向后或向前更换位置,当前位置空出;

4、松开手指,卡片可回到原位置或新位置进行展示;

整体思路:

1、卡片实行flex弹性布局,通过数组的遍历可自动显示在相应位置;

2、手指长按可使用定时器来判断,若手指松开,则关闭定时器,等待下次操作再启用;

3、跟随手指移动的卡片可使用absolute定位控制,同时根据手指位置判断当前所在位置;

4、位置发生改变时,控制数组添加或删除相应元素,从而实现换位效果;

简单效果展示:

具体实现:

一、display:flex+v-for布局:

使用弹性布局实现

<!-- 外层ul控制卡片范围 -->
<ul>
    <li class="libox" v-for="(item, ind) in list" :key="ind">
        <div>
        <!-- div显示数组内容 -->
          {{item.name}}
        </div>
    </li>
</ul>
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事件,使用定时器实现长按效果:

<div 
      @touchstart="touchstart($event, item)"
      @touchmove="touchMove($event, item)"
      @touchend="touchEnd($event, item)"
>
      {{item.name}}
</div>
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属性实现跟随手指移动效果。

<ul>
    <li v-show="selectItem.name" class="selectBox" ref="selectBox">
        {{selectItem.name}}
    </li>
</ul>
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获取:

<div 
    @touchstart="touchstart($event, item)"
    @touchmove="touchMove($event, item)"
    @touchend="touchEnd($event, item)"
    @click="listClickHandler(item)"
    v-show="item.name !== selectItem.name"
>
    {{item.name}}
</div>
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) {
        // 将新的位置赋值给全局变量oldIndex
        this.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;
  }
}

八、完整代码:

<template>
  <div>
    <ul>
      <li 
        class="libox" 
        v-for="(item, index) in list"
        :key="index"
        :id="'card' + (index + 1)"
      >
        <div
          @touchstart="touchstart($event, item)"
          @touchmove="touchMove($event, item)"
          @touchend="touchEnd($event, item)"
          v-show="item.name !== selectItem.name"
        >
          {{item.name}}
          <svg class="icon svg-icon" aria-hidden="true">
            <use :xlink:href="item.icon" rel="external nofollow" ></use>
          </svg>
        </div>
      </li>
      <li v-show="selectItem.name" class="selectBox" ref="selectBox">
        {{selectItem.name}}
        <svg class="icon svg-icon" aria-hidden="true">
          <use :xlink:href="selectItem.icon" rel="external nofollow" ></use>
        </svg>
      </li>
    </ul>
  </div>
</template>

<script>

export default {
  data() {
    return {
      // 列表数据
      list: [
        { name: '1', selected: true, icon: '#icon-mianxingbenzivg' },
        { name: '2', selected: true, icon: '#icon-mianxingchizi' },
        { name: '3', selected: true, icon: '#icon-mianxingdiannao' },
        { name: '4', selected: true, icon: '#icon-mianxingdayinji' },
        { name: '5', selected: true, icon: '#icon-mianxingdingshuqi' },
        { name: '6', selected: true, icon: '#icon-mianxingheiban' },
        { name: '7', selected: true, icon: '#icon-mianxinggangbi' },
        { name: '8', selected: true, icon: '#icon-mianxingboshimao' },
        { name: '9', selected: true, icon: '#icon-mianxingjisuanqi' },
        { name: '10', selected: true, icon: '#icon-mianxinghuaxue' },
        { name: '11', selected: true, icon: '#icon-mianxingqianbi' },
        { name: '12', selected: true, icon: '#icon-mianxingshubao' },
        { name: '13', selected: true, icon: '#icon-mianxingshuicaibi' },
        { name: '14', selected: true, icon: '#icon-mianxingtushu' },
      ],
      // 选中元素内容
      selectItem: {},
      timeOutEvent: 0,
      oldNodePos: {
        x: 0,
        y: 0,
      },
      oldMousePos: {
        x: 0,
        y: 0
      },
      oldIndex: 0,
      // 长按标识
      longClick: 0
    };
  },
  watch: {
    oldIndex(newVal) {
      const oldIndex = this.list.findIndex(r=> r.name === this.selectItem.name);
      this.list.splice(oldIndex, 1);
      this.list.splice(newVal, 0, this.selectItem);
    }
  },
  methods: {
    touchstart(ev, item) {
      this.longClick = 0;
      const that = this;
      const selectDom = ev.currentTarget; // div元素
      this.timeOutEvent = setTimeout(() => {
        that.longClick = 1;
        that.selectItem = item;
        // 元素初始位置
        that.oldNodePos = {
          x: selectDom.offsetLeft,
          y: selectDom.offsetTop
        };
        // 鼠标原始位置
        that.oldMousePos = {
          x: ev.touches[0].pageX,
          y: ev.touches[0].pageY
        };
        const lefts = that.oldMousePos.x - that.oldNodePos.x; // x轴偏移量
        const tops = that.oldMousePos.y - that.oldNodePos.y; // y轴偏移量
        const { pageX, pageY } = ev.touches[0]; // 手指位置
        that.$refs.selectBox.style.left = `${pageX - lefts}px`;
        that.$refs.selectBox.style.top = `${pageY - tops}px`;
      }, 500);
    },
    touchMove(ev) {
      clearTimeout(this.timeOutEvent);
      const selectDom = ev.currentTarget.parentNode; // li元素
      if (this.longClick === 1) {
        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`;
        this.cardIndex(selectDom, pageX, pageY);
      }
    },
    touchEnd() {
      clearTimeout(this.timeOutEvent);
      this.selectItem = {};
    },
    
    cardIndex(selDom, moveleft, movetop) {
      const liWid = selDom.clientWidth;
      const liHei = selDom.clientHeight;
      const newWidthNum = Math.ceil((moveleft / liWid)); // 哪一列
      const newHeightNum = Math.ceil((movetop / liHei)); // 哪一行
      const newPositionNum = (newHeightNum - 1) * 4 + newWidthNum;
      if (this.oldIndex !== newPositionNum - 1) {
          if (newPositionNum <= this.list.length) {
            this.oldIndex = newPositionNum - 1;
          } else {
            this.oldIndex = this.list.length - 1;
          }
      }
    }
  }
}
</script>

<style lang="sCSS" scoped>
  @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; 
      -ms-user-select:none; 
      -khtml-user-select:none; 
      user-select:none;
    }
  }
</style>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: vue实现移动端touch拖拽排序

本文链接: https://lsjlt.com/news/34267.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • vue实现移动端touch拖拽排序
    目录功能介绍:大致需求:整体思路:简单效果展示:具体实现:一、display:flex+v-for布局:二、touch事件绑定:三、卡片移动:四、获取手指所在位置:五、操作数组(删除...
    99+
    2024-04-02
  • vue实现移动端拖拽悬浮按钮
    目录功能介绍:大致需求:整体思路:具体实现:一、position:fixed布局:二、touch事件绑定:三、页面引入:本文实例为大家分享了vue实现移动端拖拽悬浮按钮的具体代码,供...
    99+
    2024-04-02
  • vue实现拖拽排序效果
    本文实例为大家分享了vue实现拖拽排序效果的具体代码,供大家参考,具体内容如下 效果预览 组件 drag.vue <template>   <Transition...
    99+
    2024-04-02
  • vue实现移动端可拖拽式icon图标
    本文实例为大家分享了vue实现移动端可拖拽式icon图标的具体代码,供大家参考,具体内容如下 需求描述:在移动端页面悬浮一个可随便拖拽的图标,类似于苹果手机的辅助触控小圆点,并且可随...
    99+
    2024-04-02
  • vue如何实现移动端拖拽悬浮按钮
    这篇文章主要讲解了“vue如何实现移动端拖拽悬浮按钮”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“vue如何实现移动端拖拽悬浮按钮”吧!功能介绍:在移动端开发中,实现悬浮按钮在侧边显示,为不...
    99+
    2023-07-02
  • jquery+css实现移动端元素拖动排序
    本文实例为大家分享了jquery+css实现移动端元素拖动排序的具体代码,供大家参考,具体内容如下 1.近期需要实现一个选项进行拖动排序的页面,页面如下: 2.JSP页面代码:...
    99+
    2024-04-02
  • jquery实现移动端悬浮拖拽框
    使用jquery 实现了一个基础的悬浮弹拖拽窗, 根据自己的需求去完善动效。 分享给大家供大家参考,具体如下: 演示效果 代码块 需要引入jquery , 引入本地或线上根据自己的...
    99+
    2024-04-02
  • Jquery实现移动端控制DIV拖拽
    本文实例为大家分享了Jquery实现移动端控制DIV拖拽的具体代码,供大家参考,具体内容如下 需求:车型配置表,移动端需要滑动,并且多项配置的表需要联动对应头部分类名称 要求:左侧 ...
    99+
    2024-04-02
  • Vue实用功能之实现拖拽元素、列表拖拽排序
    目录Vue实现拖拽元素、列表拖拽排序组件使用补充:排序动画总结Vue实现拖拽元素、列表拖拽排序 需求:    1、左右两个容器,左边和右边的元素可以拖动...
    99+
    2022-11-13
    vue列表拖拽排序 vue实现拖拽功能 vue实现组件拖拽
  • vue怎么实现列表拖拽排序效果
    这篇文章主要介绍“vue怎么实现列表拖拽排序效果”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“vue怎么实现列表拖拽排序效果”文章能帮助大家解决问题。效果如下:<template>&nb...
    99+
    2023-06-29
  • js 实现拖拽排序详情
    目录1、前言2、实现3、为何不使用HTML拖放API实现?4、总结1、前言 拖拽排序对于小伙伴们来说应该不陌生,平时工作的时候,可能会选择使用类似Sortable.js这样的开源库来...
    99+
    2024-04-02
  • vue实现图片拖动排序
    本文实例为大家分享了vue实现图片拖动排序的具体代码,供大家参考,具体内容如下 原理:现有一个图片的列表,拖动其中一个图片(触发dragstart),当拖动的图片移动到其他图片的位置...
    99+
    2024-04-02
  • vue实现移动端div拖动效果
    本文实例为大家分享了vue实现移动端div拖动的具体代码,供大家参考,具体内容如下 手机上会偶尔用到拖动div的效果,虽然我自己还没遇到,先写一个以防万一,需要注明的是,具体实现代码...
    99+
    2024-04-02
  • vue实现列表拖拽排序的示例代码
     本文主要介绍了vue实现列表拖拽排序的示例代码,具体如下: <template> <div class="test_wrapper" @drago...
    99+
    2024-04-02
  • vue实现页面div盒子拖拽排序功能
    vue 实现页面div盒子拖拽排序功能前言:目前市面上有很多实现拖拽排序功能的插件和方法,本节不过多累述,只讲一种:css3的transition-group方法 效果图: 1....
    99+
    2024-04-02
  • Vue+Element树形表格实现拖拽排序示例
    目录安装sortablejs在需要的页面引入表格加上row-key="id"树形表格排序(树结构)方法介绍注意点结语今天给大家分享一下树形表格拖拽排序,树形表格排...
    99+
    2022-11-13
    Vue Element树形表格拖拽排序 Vue Element表格拖拽
  • vue2.0如何实现移动端滑动事件vue-touch
    这篇文章主要介绍vue2.0如何实现移动端滑动事件vue-touch,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Vue-touch的使用有时候我们不止需要有返回键,也要有手势滑动切...
    99+
    2024-04-02
  • typescript+react实现移动端和PC端简单拖拽效果
    本文实例为大家分享了typescript+react实现移动端和PC端简单拖拽效果的具体代码,供大家参考,具体内容如下 一、移动端 1.tsx代码 import { Compon...
    99+
    2024-04-02
  • Vue拖拽排序组件Vue-Slicksort解读
    目录一、效果图二、安装组件三、使用组件四、组件参数五、组件方法HandleDirective总结一、效果图 二、安装组件 npm i vue-slicksort -S 三、使用组件...
    99+
    2023-03-10
    Vue拖拽排序组件 Vue-Slicksort Vue拖拽排序
  • Javascript实现拖拽排序的代码
    运行环境:vue3.2以上,复制张贴运行即可看效果效果如下: <template> <div class="container"> <tr...
    99+
    2024-04-02
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作