uniapp拖拽排序怎么实现?原生组件完美解决方案

简介

🔥 干货分享!超好用的拖拽排序菜单方案来啦! 最近为公司项目开发了一个超顺滑的拖拽排序功能,在参考了多个开源方案后,结合项目实际需求进行了全面优化。现在把这份经过实战检验的代码分享给各位开发者小伙伴~

🌟 方案亮点:

• 极致流畅的拖拽体验

• 完美适配多端平台

• 简洁易用的API设计

觉得有用的话别忘了点个赞❤️,收藏⭐️起来备用哦!遇到任何问题都欢迎在评论区留言交流~

预览图

实现思路:

复制代码
主要是通过 CSS定位 + 手指移动时的位置与元素位置进行一个比较,最后交换位置就好。

由于这边是纵轴排序没有横轴,所以判断得很少,也超级简单的啦。😆

完整代码

一、父组件

html 复制代码
<template>
	<view class="demo">
		<drag style="width: 100%; height: 100%;" :list="list" />
	</view>
</template>
 
<script>
	import drag from './drag.vue'
	export default {
		components: {
			drag
		},
		data() {
			return {
				list: [{
					id: 1,
					title: 'item1'
				}, {
					id: 2,
					title: 'item2'
				}, {
					id: 3,
					title: 'item3'
				}, {
					id: 4,
					title: 'item4'
				}, ],
			}
		},
	};
</script>
 
<style scoped lang="scss">
.demo {
	width: 90%;
	margin: 0 auto;
	padding: 0 20rpx;
	height: 500rpx;
	border: 1px solid red;
}
</style>

二、拖拽组件,可根据自己需求修改(子组件)

html 复制代码
<!--
 * @Author: jiaojs [email protected]
 * @Date: 2025-03-25 15:29:03
 * @LastEditors: jiaojs [email protected]
 * @LastEditTime: 2025-03-25 15:29:03
 * @Description: Drag and drop sorting list component encapsulation
-->
<template>
	<view class="drag-container">
		<template v-if="controlsPositionArray.length !== 0">
			<view v-for="(item, index) in controlsArray" :key="index" class="item" :style="{'transition': (curretnControlsIndex === index ? 'initial' : `${animationDuration}s`), 
				'z-index': (curretnControlsIndex === index ? 1 : 0), 
				'height': controlsSize.height + 'px', 
				'top': controlsPositionArray[index].top + 'px',  
				'left': controlsPositionArray[index].left + 'px'}">
				<view class="list-item" @touchstart="handleTouchstart($event, index)" @touchmove="handleTouchmove"
					@touchend="handleTouchend">
					<!-- 自定义内容 -->
					<view style="title">
						{{item.title}}
					</view>
				</view>
			</view>
		</template>
	</view>
</template>
 
<script>
	export default {
		name: "drag-container",
		props: {
			// 控件的大小
			controlsSize: {
				type: Object,
				default: () => ({
					height: 40
				}),
			},
			// 数据列表
			list: {
				type: Array,
				default: () => [],
			},
			/* 动画时长 */
			animationDuration: {
				type: Number,
				default: 0.3
			}
		},
		data() {
			return {
				// 控件列表
				controlsArray: [],
				// 每行最大存放的个数
				maxWidthCount: 1,
				// 控件的间距
				margin: {
					margin_x: 0,
					margin_y: 10,
				},
				// 记录所有控件的初始位置
				recordInitControlsPoisitonList: [],
				// 控件的数据
				controlsPositionArray: [],
				// 记录当前手指的位置
				recordPosition: {
					x: 0,
					y: 0,
				},
				// 记录当前操作的控件数据
				recordControlsPositionItem: {},
				// 当前操作的控件的下标
				curretnControlsIndex: -1,
			};
		},
		mounted() {
			// 获取系统信息
			this.systemInfo = uni.getSystemInfoSync();
			// 获取控件列表
			this.controlsArray = this.list;
 
			// 初始化控件的位置
			this.controlsPositionArray = this.initControlsPosition();
		},
		methods: {
			/** 初始化各个控件的位置 */
			initControlsPosition() {
				// 用于返回出去的新数组
				let tempArray = [];
 
				// 设置控件位置
				for (let i = 0, j = 0; i < this.list.length; i++, j++) {
					tempArray[i] = {
						left: this.margin.margin_x,
						top: j * (this.controlsSize.height + this.margin.margin_y) + this.margin.margin_y,
					}
				}
 
				// 记录数据 - 进行深拷贝
				this.recordInitControlsPoisitonList = [...tempArray];
				// 返回数据
				return tempArray;
			},
 
			/** 处理手指触摸后移动 */
			handleTouchmove(event) {
				const {
					pageX,
					pageY
				} = event.touches[0];
 
				// 获取移动的差
				this.controlsPositionArray[this.curretnControlsIndex] = {
					left: this.controlsPositionArray[this.curretnControlsIndex].left + (pageX - this.recordPosition.x),
					top: this.controlsPositionArray[this.curretnControlsIndex].top + (pageY - this.recordPosition.y),
				}
				// 记录位置
				this.recordPosition = {
					x: pageX,
					y: pageY
				};
				// 判断当前移动的位置是否需要进行排序
				// 向下移动
				if (this.curretnControlsIndex !== this.controlsPositionArray.length - 1 && this.controlsPositionArray[this
						.curretnControlsIndex].top > this.controlsPositionArray[this.curretnControlsIndex + 1].top) {
					// 交换位置
					this._handleChangeControlsPosition(0, this.curretnControlsIndex + 1);
				}
				// 向上移动
				else if (this.curretnControlsIndex !== 0 && this.controlsPositionArray[this.curretnControlsIndex].top <
					this.controlsPositionArray[this.curretnControlsIndex - 1].top) {
					// 交换位置
					this._handleChangeControlsPosition(0, this.curretnControlsIndex - 1);
				}
			},
 
			/** 处理手指触摸开始事件 */
			handleTouchstart(event, index) {
				const {
					pageX,
					pageY
				} = event.touches[0];
 
				// 记录一些数据
				this.curretnControlsIndex = index;
				this.recordPosition = {
					x: pageX,
					y: pageY
				};
				this.recordControlsPositionItem = this.controlsPositionArray[index];
			},
 
			/** 处理手指松开事件 */
			handleTouchend(event) {
				// 将操控的控件归位
				this.controlsPositionArray[this.curretnControlsIndex] = this.recordInitControlsPoisitonList[this
					.curretnControlsIndex];
				this.curretnControlsIndex = -1;
			},
 
			/**
			 * 处理交换控件位置的方法 - 
			 * @param {number} index	需要与第几个下标交换位置
			 * */
			_handleChangeControlsPosition(type, index) {
				// 记录当前操控的控件数据
				let tempControls = this.controlsArray[this.curretnControlsIndex];
 
				// 设置原来位置的数据
				this.controlsArray[this.curretnControlsIndex] = this.controlsArray[index];
				// 将临时存放的数据设置好
				this.controlsArray[index] = tempControls;
 
				// 调整控件位置数据
				this.controlsPositionArray[index] = this.controlsPositionArray[this.curretnControlsIndex];
				this.controlsPositionArray[this.curretnControlsIndex] = this.recordControlsPositionItem;
 
				// 改变当前选中的位置
				this.curretnControlsIndex = index;
 
				// 记录新位置的数据
				this.recordControlsPositionItem = this.recordInitControlsPoisitonList[this.curretnControlsIndex];
			},
		}
	}
</script>
 
<style scoped lang="scss">
	.drag-container {
		position: relative;
		width: 100%;
		height: 100%;
 
		.item {
			width: 100%;
			height: 100%;
			position: absolute;
 
			.list-item {
				width: 100%;
				height: 100%;
				border: 1px solid red;
 
				.title {
					display: flex;
					align-items: center;
					justify-content: center;
					width: 100%;
					height: 100%;
				}
			}
		}
	}
</style>
相关推荐
GISer_Jing13 分钟前
React-Markdown详解
前端·react.js·前端框架
太阳花ˉ14 分钟前
React(九)React Hooks
前端·react.js
拉不动的猪1 小时前
vue与react的简单问答
前端·javascript·面试
污斑兔2 小时前
如何在CSS中创建从左上角到右下角的渐变边框
前端
星空寻流年2 小时前
css之定位学习
前端·css·学习
旭久2 小时前
react+antd封装一个可回车自定义option的select并且与某些内容相互禁用
前端·javascript·react.js
是纽扣也是烤奶3 小时前
关于React Redux
前端
阿丽塔~3 小时前
React 函数组件间怎么进行通信?
前端·javascript·react.js
冴羽3 小时前
SvelteKit 最新中文文档教程(17)—— 仅服务端模块和快照
前端·javascript·svelte