使用uniapp——实现微信小程序的拖拽排序(vue3+ts)

如何实现列表排序

一、效果展示

微信小程序实现拖拽排序展示

二、实现方案

方案1. 触摸事件实现拖拽排序

使用微信小程序支持的触摸事件:

  • @touchstart
  • @touchmove
  • @touchend
    通过计算触摸位移来判断是否需要交换元素位置。

方案2. 利用按钮实现辅助排序

在每个分类项右侧添加上下箭头按钮:

  • 点击向上箭头:将当前项与上一项交换位置
  • 点击向下箭头:将当前项与下一项交换位置

三、功能特点

  • 触摸拖拽:长按并移动来调整顺序
  • 按钮操作:点击箭头按钮精确调整位置

四、原理讲解

由于微信小程序是没有dom元素的,因此我们常规的获取dom元素,并通过修改dom元素的位置的方式来实现拖拽排序已经行不通,也无法通过使用HTML5的拖拽API,因此我们需要找到适合微信小程序的方式------
触摸事件 + 数据交换来实现拖拽排序的效果。

官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html

微信小程序提供的触摸事件

  • @touchstart - 手指触摸动作开始
  • @touchmove - 手指触摸后移动
  • @touchend - 手指触摸动作结束
  1. 当触摸开始时我们利用一个变量dragIndex记录被触摸的元素的index,并利用另一个变量startY记录被触摸元素的初始的y方向上的位置。
  2. 当触摸后移动时,我们通过touchmove返回的event变量,得知当前触摸的元素移动到哪里了,并判断移动的位置和初始的位置是否达到了该交换元素的值了,如果到达,就开始交换两个元素。交换之前,应该先判断触摸元素的移动方向,如果是移动的方向是向上,则让当前索引的元素和上方的元素交换,移动方向是向下,让当前索引的元素和下方的元素交换。
  3. 交换的方式则是利用索引,交换数组中两个元素
  4. 如果是辅助按钮的方式移动,则直接根据移动方向,与上方或下方元素进行交换位置。

五、问题

到目前为止,我们在拖动的时候,会出现页面跟着滚动的情况,如何防止在拖动的时候,防止页面跟着滚动呢?

在拖动时想要禁止页面滚动就让父盒子的catchtouchmove="true"

六、完整代码

html 复制代码
<!-- 拖拽排序区域 -->
    <view v-if="showSortMode" class="p-4" catchtouchmove="true">
      <view class="space-y-2">
        <view 
          v-for="(item, i) in categories" 
          :key="item.id"
          :class="[
            'flex items-center justify-between bg-white rounded-lg p-4 border border-gray-200 transition-all duration-200',
            dragIndex === i ? 'opacity-50 scale-95' : ''
          ]"
          @touchstart="handleTouchStart($event, i)"
          @touchmove="handleTouchMove($event, i)"
          @touchend="handleTouchEnd($event, i)"
        >
          <view class="flex items-center gap-3">
            <wd-icon name="drag" size="16px" color="#999" />
            <text class="text-gray-800">{{ item.name }}</text>
            <text class="text-xs text-gray-500">({{ item.count }})</text>
          </view>
          <view class="flex items-center gap-2">
            <wd-icon name="arrow-up" size="14px" color="#666" @click="moveUp(i)" v-if="i > 0" />
            <wd-icon name="arrow-down" size="14px" color="#666" @click="moveDown(i)" v-if="i < categories.length - 1" />
          </view>
        </view>
      </view>
    </view>
    
<script setup lang="ts">

let dragIndex = ref(-1); // 当前拖拽的元素索引值
const showSortMode = ref(false); // 控制排序模式显示
let startY = 0; // 触摸开始的Y坐标
let currentY = 0; // 当前触摸的Y坐标

// 触摸开始
const handleTouchStart = (e: any, index: number) => {
  dragIndex.value = index;
  startY = e.touches[0].clientY;// 当前触摸元素的初始触摸位置
};

// 触摸移动
const handleTouchMove = (e: any, index: number) => {
  currentY = e.touches[0].clientY;
  const deltaY = currentY - startY;
  
  // 根据移动距离判断是否需要交换位置
  if (Math.abs(deltaY) > 50) { // 移动超过50px才触发交换
    const targetIndex = deltaY > 0 ? index + 1 : index - 1;
    if (targetIndex >= 0 && targetIndex < categories.value.length && targetIndex !== dragIndex.value) {
      swapItems(dragIndex.value, targetIndex);
      dragIndex.value = targetIndex;
      startY = currentY; // 重置起始位置
    }
  }
};

// 触摸结束
const handleTouchEnd = (e: any, index: number) => {
  dragIndex.value = -1;
  startY = 0;
  currentY = 0;
};

// 交换数组中两个元素的位置
const swapItems = (fromIndex: number, toIndex: number) => {
  [categories.value[fromIndex], categories.value[toIndex]] = [categories.value[toIndex], categories.value[fromIndex]];
};

// 向上移动
const moveUp = (index: number) => {
  if (index > 0) {
    swapItems(index, index - 1);
  }
};

// 向下移动
const moveDown = (index: number) => {
  if (index < categories.value.length - 1) {
    swapItems(index, index + 1);
  }
};

interface Category {
  id: string;
  name: string;
  count: number;
}

const categories = ref<Category[]>([
  { id: '1', name: '饮料', count: 12 },
  { id: '2', name: '小食', count: 8 },
  { id: '3', name: '主食', count: 15 },
  { id: '4', name: '甜品', count: 6 },
  { id: '5', name: '热饮', count: 9 },
]);
</script>
相关推荐
游戏开发爱好者818 小时前
App Store 上传 IPA 自动化,.p8 密钥认证与 CI/CD 接入实战
android·运维·ci/cd·小程序·uni-app·自动化·iphone
Greg_Zhong18 小时前
微信小程序 + 腾讯云人体分析:从 0 到 1 实现 AI 抠图打卡合照(细节待更新~)
人工智能·微信小程序·腾讯云·ai抠图-ai打卡拍照
游戏开发爱好者819 小时前
iOS 推送怎么配置,APNs 推送证书、设备库与群发
android·ios·小程序·https·uni-app·iphone·webview
AI_AGENT_DEV_AI1 天前
原生 APP 开发的开发优点
uni-app
TELL5212 天前
vue前端架构
vue
JackieDYH2 天前
uniapp-通知滚动组件+轮播图组件封装-案例
uni-app·轮播图·通知
南城以南溫暖如初1472 天前
社区健身小程序开发流程详解:技术选型与实施经验分享
java·经验分享·spring boot·mysql·vue·apache
万亿少女的梦1682 天前
基于Spring Boot、Vue与MySQL的私人健身教练预约管理系统设计
java·spring boot·mysql·vue·预约管理
2501_915921432 天前
appuploader-cli 命令行上传 IPA 到 App Store Connect upload CI 集成
android·ci/cd·小程序·https·uni-app·iphone·webview
结网的兔子3 天前
【前端开发】UniApp 项目地图选型与Web-APP跨端迁移方案
前端·uni-app