uniapp 小程序端简单实现一个拖动排序

uniapp 小程序端简单实现一个拖动排序

在小程序端实现这个功能没有 web 端那么方便,在插件市场也没有找到合适的,所以简单实现一个。

我们知道,一般来说要实现拖动排序的列表大概率没有很长很长,所以就不做长列表的优化了。

微信小程序可以单独使用 wxs 优化

当然实现方式多样,适合自己的才是最好的。

简单的部分原理说明

通常来说,部分列表项拥有自己特定的响应事件,例如点击操作,我们实现拖动最好和这些事件避开。

首先确定列表项的排列方式,我们选择的是定位来布局列表项,因为这样方便操作每个元素的位置,直接动态改变元素的定位信息即可。

接着就是怎么确定元素的位置信息,包括后面如何判断拖动元素位于某个元素内部,基于这些,我们就必须知道列表项的宽高信息,基于列表项的宽高,就可以很好计算这些信息。所以,得定宽高!

宽高有了就不分析了~~~

实现

<template>
    <view>
        <view
            class="test-list"
            @touchstart="handleStart"
            @touchmove="handleMove"
            @touchend="handleEnd"
            :style="{
                overflowY: currItem ? 'hidden' : 'scroll'
            }"
        >
            <view
                class="test-item test-item-base"
                v-for="item in dataList"
                :key="item.id"
                @longpress="handleLongpress(item)"
                :style="{
                    height: height + 'px',
                    width: width + 'px',
                    opacity: item.id === currItem.id ? 0.4 : 1,
                    top: item.y + 'px',
                    left: item.x + 'px'
                }"
            >
                <text>{{ item.title }}</text>
            </view>
            
            <view
                class="test-item"
                v-if="currItem"
                :style="{
                    height: height + 'px',
                    width: width + 'px',
                    top: currItem.y + 'px',
                    left: currItem.x + 'px'
                }"
            >
                <text>{{ currItem.title }}</text>
            </view>
        </view>
    </view>
</template>

<script>
    const BASE_MARGIN = uni.upx2px(30)
    const BASE_MARGIN_TOP = uni.upx2px(20)
    
    export default {
        data() {
            return {
                height: uni.upx2px(400),
                width: uni.upx2px(330),
                windowHeight: 0,
                windowWidth: 0,
                dataList: [],
                currItem: null,
                startTouch: {
                    x: 0,
                    y: 0
                },
                sortLoading: false
            }
        },
        methods: {
            // 手指按下,记录初始位置信息,用于计算当前元素移动距离
            handleStart(e) {
                let { clientX: x, clientY: y } = e.touches[0]
                this.startTouch = { x, y }
            },
            // 手指拖动排序
            handleMove(e) {
                if (!this.currItem) return
                let { clientX: x, clientY: y } = e.touches[0]
                this.currItem.x += x - this.startTouch.x
                this.currItem.y += y - this.startTouch.y
                let tIndex = this.dataList.findIndex(o => o.id == this.currItem.id)
                
                if (tIndex > -1) {
                    // 判断拖动中的元素位于哪个元素内部
                    let i = this.dataList.findIndex(item => {
                        // 可优化
                        return (
                            // 当前元素左上角位于元素内部
                            this.currItem.x <= (item.x + this.width / 2) &&
                            this.currItem.y <= (item.y + this.height / 2) &&
                            this.currItem.y >= item.y &&
                            this.currItem.x >= item.x
                        ) || (
                            // 当前元素左下角位于元素内部
                            this.currItem.x >= item.x &&
                            this.currItem.x <= (item.x + this.width / 2) &&
                            (this.currItem.y + this.height) >= (item.y + this.height / 2) &&
                            (this.currItem.y + this.height) <= (item.y + this.height)
                        ) || (
                            // 当前元素右上角位于元素内部
                            this.currItem.y >= item.y &&
                            this.currItem.y <= (item.y + this.height / 2) &&
                            (this.currItem.x + this.width) >= (item.x + this.width / 2) &&
                            (this.currItem.x + this.width) <= (item.x + this.width)
                        ) || (
                            // 当前元素右下角位于元素内部
                            (this.currItem.y + this.height) >= (item.y + this.height / 2) &&
                            (this.currItem.y + this.height) <= (item.y + this.height) &&
                            (this.currItem.x + this.width) >= (item.x + this.width / 2) &&
                            (this.currItem.x + this.width) <= (item.x + this.width)
                        )
                    })
                    
                    if (i != tIndex && i > -1) {
                        let tItem = this.dataList[tIndex]
                        // 简单实现元素位置更改
                        this.dataList.splice(tIndex, 1)
                        this.dataList.splice(i, 0, tItem)
                        
                        // 优化,改变位置的元素始终都是当前元素和被拖到的位置元素之间的元素
                        let maxI = i > tIndex ? i : tIndex
                        let minI = i > tIndex ? tIndex : i
                        
                        while (minI <= maxI) {
                            let { px, py } = this.handlePosition(minI)
                            let item = this.dataList[minI]
                            item.x = px
                            item.y = py
                            // let _index = minI + 1
                            // let item = this.dataList[minI]
                            // if (_index % 2 === 0) {
                            // 	item.x = this.windowWidth - BASE_MARGIN - this.width
                            // } else {
                            // 	item.x = BASE_MARGIN
                            // }
                            
                            // if (_index <= 2) {
                            // 	item.y = BASE_MARGIN_TOP
                            // } else {
                            // 	item.y = (Math.ceil(_index / 2) - 1) * (BASE_MARGIN_TOP + this.height) + BASE_MARGIN_TOP
                            // }
                            minI++
                        }
                        
                        // this.dataList.forEach((item, index) => {
                        // 	let _index = index + 1
                        // 	if (_index % 2 === 0) {
                        // 		item.x = this.windowWidth - BASE_MARGIN - this.width
                        // 	} else {
                        // 		item.x = BASE_MARGIN
                        // 	}
                            
                        // 	if (_index <= 2) {
                        // 		item.y = BASE_MARGIN_TOP
                        // 	} else {
                        // 		item.y = (Math.ceil(_index / 2) - 1) * (BASE_MARGIN_TOP + this.height) + BASE_MARGIN_TOP
                        // 	}
                        // })
                    }
                    
                    this.startTouch = { x, y }
                }
                
            },
            // 手指拖动结束,清空当前拖动的元素,开始进行排序确定
            handleEnd() {
                if (!this.currItem) return
                this.currItem = null
                this.handleSort()
            },
            // 模拟排序
            handleSort() {
                if (this.sortLoading) return
                this.sortLoading = true
                // 向后端排序接口发起请求
                setTimeout(() => {
                    this.sortLoading = false
                }, 100)
            },
            // 长按选中当前要进行拖动排序的元素
            handleLongpress(item) {
                if (this.currItem != null || this.sortLoading) return
                this.currItem = JSON.parse(JSON.stringify(item))
                this.currItem.x = this.currItem.x + 10
                this.currItem.y = this.currItem.y + 10
            },
            // 计算元素位置信息,通过当前元素的索引和初始化宽高来计算
            handlePosition(i) {
                let px = 0
                let py = 0
                let _index = i + 1
                
                if (_index % 2 === 0) {
                    px = this.windowWidth - BASE_MARGIN - this.width
                } else {
                    px = BASE_MARGIN
                }
                
                if (_index <= 2) {
                    py = BASE_MARGIN_TOP
                } else {
                    py = (Math.ceil(_index / 2) - 1) * (BASE_MARGIN_TOP + this.height) + BASE_MARGIN_TOP
                }
                
                return { px, py }
            }
        },
        created() {
            let systemInfo = uni.getSystemInfoSync()
            this.windowHeight = systemInfo.windowHeight
            this.windowWidth = systemInfo.windowWidth
            
            // 模拟获取到数据并对数据进行处理
            for (let i = 0; i < 10; i++) {
                // let x = 0
                // let y = 0
                // let _index = i + 1
                
                // if (_index % 2 === 0) {
                // 	x = this.windowWidth - BASE_MARGIN - this.width
                // } else {
                // 	x = BASE_MARGIN
                // }
                
                // if (_index <= 2) {
                // 	y = BASE_MARGIN_TOP
                // } else {
                // 	y = (Math.ceil(_index / 2) - 1) * (BASE_MARGIN_TOP + this.height) + BASE_MARGIN_TOP
                // }
                let { px, py } = this.handlePosition(i)
                this.dataList.push({ id: i, title: `测试${i}`, x: px, y: py })
            }
        }
    }
</script>

<style scoped lang="scss">
    .test-list {
        position: relative;
        height: 100vh;
    }
    .test-item {
        position: absolute;
        background-color: orange;
    }
    .test-item-base {
        transition: all .5s;
    }
</style>

效果

CPT2401191814-390x844.gif

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值