小程序---小程序列表项拖拽排序

article/2025/11/6 12:19:23

一、拖拽效果图展示

首先,上个gif图看看效果

吐血测试了一天,目前还未发现bug。ps(拖拽效果仅在前端实现,未和后端交互)

文章代码参考小程序实现列表拖拽排序 ,参考文章还是存在一些bug和不足,比如,样式代码没给对,还有实现的拖拽是拖拽起始位置数据的替换,不是我想要的拖拽效果。

二、解决思路

首先,拖拽重要的计算清楚位置关系,从什么地方开始拖,拖到什么地方结束,以及如何判断有效的拖拽排序。

在拖拽开始时、拖拽过程中、拖拽结束时获取拖拽数据项的索引和实时的位置相关数据。给拖拽节点绑定catchtouchstart、catchtouchmove、catchtouchend事件,利用data-xxx属性传递索引)

  <!-- 拖拽节点 --><view data-index='{{index}}' catchtouchstart='dragStart' catchtouchmove='dragMove'catchtouchend='dragEnd'><image class="img" src="/image/icon-sort.png"></image></view>

结合下图进行分析,如图,需要拖拽的数据列表都放在class=habitlist的节点中,为方便计算位置,固定每个数据项的height=60px;假设数据列表habitList=[{ name: "跳舞",icon: "./image/icon/icon-dance.png"},{name:"看书",...},...],当前拖拽项(name为跳舞的项)的克隆为kelong={name: "跳舞",icon: "./image/icon/icon-dance.png"}

1、【catchtouchstart】-拖拽开始时,获取当前拖拽项的索引,克隆当前项的内容给对象kelong。通过节点查询器,选择class=habitlist的节点,获取节点位置信息的查询请求,rect.top为该节点距离顶部的距离;rec.height为该节点区域的高度;e.changedTouches[0].clientY代表当前点击位置距离可视区域顶部的垂直距离,由于每个数据项的height=60px;预估:top=.changedTouches[0].clientY - rect.top - 30为当前克隆项显示的top,注意:克隆项显示的位置要相对class=habitlist的节点定位(子绝父相)。该值也为拖拽开始时的kelong项的位置startTop,(startTop值主要用于拖拽结束时判断是向下还是向上拖拽了)

  // 拖拽开始dragStart(e) {// console.log("拖拽开始", e);var i = e.currentTarget.dataset.index // 当前拖拽项的索引index// 把当前拖拽项的内容复制给kelongvar kelong = this.data.habitList[i]var query = wx.createSelectorQuery()  // 创建节点查询器 quer//选择class=habitlist的节点,获取节点位置信息的查询请求query.select('.habitlist').boundingClientRect((rect) => {var top = e.changedTouches[0].clientY - rect.top - 30var startTop = top;this.setData({kelong: kelong,selectedIndex:i,showkelong: true,top:top,startTop:startTop})}).exec();},

2、【catchtouchmove】-拖拽移动时,要对拖拽的范围做控制,文中做了顶部边界控制:控制克隆项不会拖拽出class=habitlist节点的顶部边界。当top<0时,将top置为0,底部可不做限制。

  // 拖拽移动dragMove(e) {// console.log("拖拽移动", e);var query = wx.createSelectorQuery();var top =this.data.topquery.select('.habitlist').boundingClientRect((rect) => {top = e.changedTouches[0].clientY - rect.top - 30if (top < 0) {// 顶部边界控制:控制克隆项不会拖拽出class=habitlist节点的顶部边界top = 0}this.setData({top:top})}).exec();},

3、【catchtouchend】-拖拽结束时,

(1)进行边界控制:不仅要进行顶部边界限制,还要做底部边界控制,即如果拖拽超出底部边界,则该拖拽数据项为最后一个位置的数据了。

(2)向上或向下拖拽的数据备份及处理:向上拖拽时,需要备份插入位置target开始的下方数据(除了拖拽数据项);向下拖拽时,需要备份插入位置target开始的上方数据;然后按拖拽后的正确索引位置遍历赋值。

// 拖拽结束dragEnd(e) {// console.log("拖拽结束", e);var i = e.currentTarget.dataset.indexvar query = wx.createSelectorQuery();var kelong = this.data.kelongvar habitList = this.data.habitListquery.select('.habitlist').boundingClientRect((rect) => {var top = e.changedTouches[0].clientY - rect.top - 30if (top > rect.height) {// 底部边界控制:控制克隆项拖拽结束时不会出class=habitlist节点的底部边界top = rect.height - 60} else if (top < 0) {// 顶部边界控制:控制克隆项拖拽结束时不会出class=habitlist节点的顶部边界top = 0}this.setData({top: top,})var target = parseInt(top / 60)var list = []  //用于备份数据if (this.data.startTop > top) {//  往上方位置拖拽for (var k = 0; k <= i - target; k++) {//  备份插入位置target开始的下方数据,除了拖拽数据项if (habitList[target + k].name != kelong.name) {list.push(habitList[target + k])}}if (list.lenghth != 0) {habitList[target] = kelongfor (var m = target + 1, n = 0; n < list.length; m++, n++) {habitList[m] = list[n]}}} else {// 往下边位置拖拽for (var k = 1; k <= target - i; k++) {//  备份插入位置target开始的上方数据,除了拖拽数据项if (habitList[i + k].name != kelong.name) {list.push(habitList[i + k])}}if (list.length != 0) {habitList[target] = kelongfor (var m = i, n = 0; n < list.length; m++, n++) {habitList[m] = list[n]}}}this.setData({habitList: habitList,selectedIndex:-1,showkelong: false})}).exec();},

三、完整代码

【sortHabit.wxml】

<view  class="page-wrapper"><top-title toptitle="排序习惯" backImgFlag="true"></top-title><view class="habitlist"><!-- 克隆当前拖拽的项 --><view class='habit kelong' hidden='{{!showkelong}}' style='top:{{top}}px'><view class='index'>?</view><view class="icon"><image class="iconImg" src="{{kelong.icon}}"></image></view><view class="info"><view class="title">{{kelong.name}}</view></view><image class="img"src="cloud://xbd-cloud-xxx/icon/icon-sort.png"></image></view><block wx:for="{{habitList}}" wx:key="name"><view class="habit {{selectedIndex==index?'gray':''}}" ><view class='index'>{{index+1}}</view><view class="icon"><image class="iconImg" src="{{item.icon}}"></image></view><view class="info"><view class="title">{{item.name}}</view></view><!-- 拖拽节点 --><view data-index='{{index}}' catchtouchstart='dragStart' catchtouchmove='dragMove'catchtouchend='dragEnd'><image class="img"src="cloud://xbd-cloud-xxx/icon/icon-sort.png"></image></view></view></block></view>
</view>

【sortHabit.js】

Page({/*** 页面的初始数据*/data: {habitList: [],// 当前拖拽项的克隆kelong: {name: '',icon: ''},startTop: 0, //拖拽开始时克隆项距离class=habitlist节点顶部边界的值top: 0,selectedIndex: -1, //被选择拖拽的项的indexbackupList: [], //用于备份数据showkelong: false, //是否显示克隆项},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {var list = [{name: "跳舞",icon: "cloud://xbd-cloud-xxx/icon/icon-dance.png"}, {name: "看书",icon: "cloud://xbd-cloud-xxx/icon/icon-reading.png"}, {name: "运动",icon: "cloud://xbd-cloud-xxx/icon/icon-sports.png"}, {name: "护肤",icon: "cloud://xbd-cloud-xxx/icon/icon-facialmask.png"}]this.setData({habitList: list})},// 拖拽开始dragStart(e) {// console.log("拖拽开始", e);var i = e.currentTarget.dataset.index // 当前拖拽项的索引index// 把当前拖拽项的内容复制给kelongvar kelong = this.data.habitList[i]console.log("拖拽开始i=",i,"kelong=",kelong);var query = wx.createSelectorQuery(); // 创建节点查询器 quer//选择class=habitlist的节点,获取节点位置信息的查询请求query.select('.habitlist').boundingClientRect((rect) => {var top = e.changedTouches[0].clientY - rect.top - 30var startTop = top;this.setData({kelong: kelong,selectedIndex:i,showkelong: true,top:top,startTop:startTop})}).exec();},// 拖拽移动dragMove(e) {// console.log("拖拽移动", e);var query = wx.createSelectorQuery();var top =this.data.topquery.select('.habitlist').boundingClientRect((rect) => {top = e.changedTouches[0].clientY - rect.top - 30if (top < 0) {// 顶部边界控制:控制克隆项不会拖拽出class=habitlist节点的顶部边界top = 0}this.setData({top:top})}).exec();},// 拖拽结束dragEnd(e) {// console.log("拖拽结束", e);var i = e.currentTarget.dataset.indexvar query = wx.createSelectorQuery();var kelong = this.data.kelongvar habitList = this.data.habitListquery.select('.habitlist').boundingClientRect((rect) => {var top = e.changedTouches[0].clientY - rect.top - 30if (top > rect.height) {// 底部边界控制:控制克隆项拖拽结束时不会出class=habitlist节点的底部边界top = rect.height - 60} else if (top < 0) {// 顶部边界控制:控制克隆项拖拽结束时不会出class=habitlist节点的顶部边界top = 0}this.setData({top: top,})var target = parseInt(top / 60)var list = []  //用于备份数据if (this.data.startTop > top) {//  往上方位置拖拽for (var k = 0; k <= i - target; k++) {//  备份插入位置target开始的下方数据,除了拖拽数据项if (habitList[target + k].name != kelong.name) {list.push(habitList[target + k])}}console.log("往上拖拽 list=======", list);if (list.lenghth != 0) {habitList[target] = kelongfor (var m = target + 1, n = 0; n < list.length; m++, n++) {habitList[m] = list[n]}}} else {// 往下边位置拖拽for (var k = 1; k <= target - i; k++) {//  备份插入位置target开始的上方数据,除了拖拽数据项if (habitList[i + k].name != kelong.name) {list.push(habitList[i + k])}}console.log("往下拖拽 list=======", list);if (list.length != 0) {habitList[target] = kelongfor (var m = i, n = 0; n < list.length; m++, n++) {habitList[m] = list[n]}}}console.log(habitList);this.setData({habitList: habitList,selectedIndex:-1,showkelong: false})}).exec();},})

【sortHabit.wxss】

.habit {margin: 0rpx auto;width: 100%;height: 60px;border-radius: 10rpx;background-color: #fff;display: flex;padding: 15rpx;box-sizing: border-box;position: relative;border-bottom: 1px solid #f1f1f1;
}
.habit .index {margin: 25rpx 20rpx 0px 30rpx;font-weight: bold;color: #c93a3a;
}
.habit .icon {margin-right: 25rpx;
}
.habit .icon .iconImg {margin-top: 10rpx;width: 70rpx;height: 70rpx;
}
.habit .info {display: flex;flex-direction: column;width: 60vw;
}
.habit .info .title {margin: 25rpx 0rpx;font-size: 30rpx;
}
.habit .img {position: absolute;right: 30rpx;margin-top: 25rpx;width: 40rpx;height: 40rpx;
}
.habitlist {position: relative;
}
.kelong {z-index: 999;position: absolute;box-shadow: 1px 1px 5px #ccc;
}
.gray {background-color: #efecec;
}

ps:也不知道自己讲清楚没,记录点滴心路历程~下篇讲讲日历打卡


http://chatgpt.dhexx.cn/article/eecjkX1i.shtml

相关文章

微信小程序列表页

我们在做Android开发时,几乎每个app都有几个列表,在Android中列表一般是用listview&#xff0c;后来就使用recyclerview做了,不管是小程序还是Android或者ios&#xff0c;列表都是常见的一种数据展示方式,那么在小程序中怎么实现呢&#xff1f;先使用最笨的方法做, 如图&#…

小程序列表展示

小程序列表展示 wxml代码 <view class"content"><form catchsubmit"formSuos" catchreset"formReset"><view class"group"><view class"sous"><input class"text1_3" auto-focus pl…

微信小程序信息展示列表

微信小程序信息展示列表 效果展示: 代码展示: wxml <view class"head"><view class"head_item">分类</view><view class"ring"></view><view class"head_item">价格</view> </view>…

微信小程序-列表展示、条目点击。

直接贴图&#xff1a; wx:for"{{datas}}"设置数据源&#xff1b; wx:for-item"item"设置条目名称&#xff1b; data-index{{index}}设置角标。 bindtapitemClick点击事件。 {{item.item}}显示数据 datas数据源。 条目点击 itemClick: function(e) { …

【微信小程序】-- 案例 - 本地生活(列表页面)(三十)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

微信小程序删除list指定列表项

一、删除效果展示&#xff1a; // 重要代码片段async deleteListItem(e) {const sureResult await wx.showModal({title: "提示",content: "确定要删除这项吗",});if (sureResult.confirm) {const { index } e.currentTarget.dataset;setTimeout(()>{…

【小程序从0到1】小程序常用组件一览

欢迎来到我的博客 &#x1f4d4;博主是一名大学在读本科生&#xff0c;主要学习方向是前端。 &#x1f36d;目前已经更新了【Vue】、【React–从基础到实战】、【TypeScript】等等系列专栏 &#x1f6e0;目前正在学习的是&#x1f525; R e a c t / 小程序 React/小程序 React/…

微信小程序商城系列之商品列表页(一)

微信小程序商城列表页 wxml: <view classlist><block wx:for{{dataList}} wx:keylist wx:for-item"item"><view class"list_item"><navigator urldetails?id{{item.goods_id}}><view classimg><image src"{{imghr…

微信小程序——商品列表

主页面 <view class"container"><googsList goodsClassList"{{goodsClassList}}" goodsList"{{goodsList}}" bind:leftClick"leftClick" bind:toDetails"toDetails" bind:scrolltolowerGoodsClass"scroll…

微信小程序 - 商城项目 - 列表排序

sort ( ) 排序 需要实现两个排序功能&#xff0c;价格排序和好评排序&#xff0c;且都可以正反切换 数字比较 价格排序中&#xff0c;价格为数字类型&#xff0c;属于数字的比较&#xff0c;无需转换类型 // 点击价格排序的图标handlePrice(){this.setData({priceOrder:!this…

微信小程序之多列表的显示和隐藏功能【附源码】

效果图&#xff1a; 实现思路&#xff1a; 实现单个列表的显示和隐藏应该使用唯一元素让程序知道你应该显示和隐藏哪个列表项&#xff0c;可以用数据的id&#xff1b;css中定义一个hidden{display&#xff1a;none}控制显示和隐藏&#xff0c;然后通过三元运算符来判断&#x…

微信小程序列表实现

1.在 .json文件中使用 navigationBarTitleText定义当前窗口title {"navigationBarTitleText": "小程序列表实现" }2.在 .wxss文件中编写如下&#xff1a; // page设置当前窗口背景色 page{background-color: #F5F5F5 }// 定义.container设置容器样式 .co…

微信小程序 开发列表

一.知识点 (一).列表渲染 wx:for tip:wx:for“array”可以等于参数名&#xff0c;在js中调用 Page({ data:{ array: [{name: 小李},{ name: 小高}]} })&#xff0c;获取值&#xff1b;也可以直接把wx:for"{{[1, 2, 3]}}"&#xff0c;把值放在上面 1.在组件上使用wx:f…

微信小程序实现分类列表

1、效果展示 2、fenleiliebiao.wxml文件代码 <!-- 头部搜索 --> <view class"title_search"></view> <!-- 头部搜索 --> <!-- Tab布局 --> <view catchtouchmovetrue><view classnavBox><view classtitleBox1 id"…

微信小程序--数据列表

设置页面标题 首页可以使用&#xff1a; app.json中window对象的"navigationBarTitleText"参数进行标题设置&#xff0c; 每个页面也可以在对应json文件中使用"navigationBarTitleText"参数进行标题设置 也可以在js文件的onRead函数中使用&#xff1a;js中…

用小程序完成简单的详情列表功能

最近学习了微信小程序&#xff0c;写了一个详情列表 主要思路是&#xff1a; 1.先通过接口拿到数据&#xff0c;在onLoad(options){}中调用wx.request(){}函数&#xff0c;获取到数据 2.完成数据渲染后&#xff0c;&#xff0c;用catchtab给它绑定一个点击事件&#xff0c;点…

微信小程序--列表展示

小知识&#xff1a; wx:for"{{list}}"用来循环数组。 wx:for-item‘变量名&#xff08;随便起&#xff09;’ 它是指定循环数据当前的变量名&#xff0c;可以通过 {{变量名.属性}} 展示数组的元素。 wx:for-index‘变量名&#xff08;随便起&#xff09;’&#x…

vmware 下 ubuntu 双网卡设置

写在前面&#xff1a; 本文章旨在总结备份、方便以后查询&#xff0c;由于是个人总结&#xff0c;如有不对&#xff0c;欢迎指正&#xff1b;另外&#xff0c;内容大部分来自网络、书籍、和各类手册&#xff0c;如若侵权请告知&#xff0c;马上删帖致歉。 目录 step 1&#xf…

Ubuntu18.04 配置网卡

2019/10/29, Ubuntu Server 18.04 摘要&#xff1a;Ubuntu Server 18.04 采用netplan作为网络配置管理&#xff0c;修改IP使其连上网络&#xff0c;修改替换软件源 修改网卡配置# 首先使用ip a查看当前网卡名称&#xff1a; 可以看到eth0就是我们的网卡名称&#xff0c;这个要…

linux Ubuntu网卡配置,Windows 7下用VirtualBox安装Ubuntu网卡配置

Windows 7下用VirtualBox安装Ubuntu&#xff0c;要求Ubuntu能连上互联网&#xff0c;同时在Ubuntu中配置SSH、FTP、NFS服务&#xff0c;能从Win7进行访问。 此时Linux虚拟机需要两块网卡&#xff0c;一块使用NAT方式(网络地址翻译网络)连接&#xff0c;用于连接互联网&#xff…