微信小程序拖拽排序功能的實現


先看下實現效果:

 

 

 

 

實現方式:

小程序movable-area+css定位和過渡。

github: GXYOG/wxapp-drag: 微信小程序拖拽排序 (github.com)

 

 

wxml代碼:

<view class="container">
<movable-area class="item_box" style="width: {{boxWeight}}rpx;height: {{boxHeight}}rpx">
<movable-view class="item {{selectId === item.id?'item_show':'item_hide'}}" wx:for="{{healthItem}}" x="{{item.x}}rpx" y="{{item.y}}rpx" direction="all" bindchange="touchMove" bindtouchend="touchend" data-index="{{item.index}}" data-id="{{item.id}}" bindtouchstart="unlockItem">
<view class="item_name">{{item.name}}</view>
</movable-view>
</movable-area>

<view class="item_box layer_box" style="width: {{boxWeight}}rpx;height: {{boxHeight}}rpx">
<view class="item layer_item {{selectId == item.id?'item_hide':''}}" wx:for="{{layerItem}}" style="left: {{item.x}}rpx;top: {{item.y}}rpx">
<view class="item_name">{{item.name}}</view>
</view>
</view>
</view>

這里的兩個item_box模塊是完全重疊的,movable以完全透明狀態在上方,當點擊到某個滑塊時,movable的此滑塊顯示,layer_box中的此滑塊隱藏。這樣做的原因是如果在滑動過程中改變movable中數據的位置,會影響到當前正在進行的滑動,不得已想出這種辦法。如果大家有別的實現方式,可以在評論告訴我。

 

js代碼:

const app = getApp();
Page({
data: {
arr: [
{
id: 1,
name: '1號'
},
{
id: 2,
name: '2號'
},
{
id: 3,
name: '3號'
},
{
id: 4,
name: '4號'
},
{
id: 5,
name: '5號'
},
{
id: 6,
name: '6號'
},
],
boxWeight: 750, //容器寬度,100%為750,單位rpx
boxHeight: 0, //容器高度
height: 352, //滑塊總高度,即滑塊本身加上邊距的高度
selectId: 0, //當前選中滑塊的id
col: 3, //滑塊列數
},
onLoad: function (options) {

},

onShow() {
let {arr,height,boxWeight,col,boxHeight} = this.data;
arr.forEach((item,i) => {
item.x = (i % col) * Math.trunc(boxWeight / col) //區域左上角橫坐標
item.y = Math.trunc(i / col) * height //區域左上角縱坐標
item.index = i;
})
if (Math.trunc(arr.length % col)){
boxHeight = (Math.trunc(arr.length / col) + 1) * height
}else{
boxHeight = Math.trunc(arr.length / col) * height
}
this.setData({
healthItem: arr,
layerItem: arr,
boxHeight,
})
},


/**
* 點擊到滑塊時切換隱藏顯示
*/
unlockItem(e){
this.setData({
selectId: e.currentTarget.dataset.id
})
},

/**
* 拖動滑塊
*/
touchMove(e){
const s = this;
let {boxWeight,height,layerItem,col} = s.data
let weight = Math.trunc(boxWeight / col); //每塊區域的寬度
if (e.detail.source === 'touch'){
let arr = [...layerItem];
let id = e.currentTarget.dataset.id;
let centerX = (e.detail.x * 2) + (weight / col) //當前選中滑塊的中心的x坐標
let centerY = (e.detail.y * 2) + (height / col) //當前選中滑塊的中心的y坐標
let key = 0; //滑塊滑動時的位置
let index = 0; //滑塊滑動前的位置

//通過id判斷當前滑塊的index
layerItem.forEach(item => {
if (item.id === id){
index = item.index
}
})

//根據當前滑塊位置確認當前所處在哪個區域
for (let i = 0; i < arr.length + 1; i++){
let x1 = (i % col) * (boxWeight / col) //第n個區域的左上角和左下角x坐標
let x2 = (i % col + 1) * (boxWeight / col) //第n個區域的右上角和右下角x坐標
let y1 = Math.trunc(i / col) * height //第n個區域的左上角和右上角y坐標
let y2 = Math.trunc(i / col + 1) * height //第n個區域的左下角和右下角y坐標
//判斷當前滑塊所屬區域
if (centerX > x1 && centerX < x2 && centerY > y1 && centerY < y2){
key = i
}
}
//當key值大於數組長度時,即數組長度為奇數,滑塊位於容器右下方無滑塊的位置,滑塊實際的key值為數組長度減一
if (key >= arr.length - 1){
key = arr.length - 1
}

//滑動時位置與滑動前不同時
if (index != key){
//計算數組中其他數據變化后的index
arr.forEach((item,i) => {
if (item.id != id){
//index前進到key位置
if (index > key){
if (item.index >= key && item.index < index){
item.index = item.index + 1
}
}
//index后退到key位置
if (index < key){
if (item.index > index && item.index <= key){
item.index = item.index - 1
}
}
}else{
item.index = key
}
})

//根據數據變化后的index計算改變順序后的實際位置
arr.forEach((item,i) => {
item.x = (item.index % col) * (boxWeight / col)
item.y = Math.trunc(item.index / col) * height
})

s.setData({
layerItem: arr,
key,index
})
}
}
},

/**
* 停止拖動,兩數組同步
*/
touchend(e){
let {layerItem} = this.data;
this.setData({
healthItem: layerItem,
})
},

});

以上涉及到長度的變量,單位皆為rpx。
大家主要看代碼注釋,這里就不多講了,有不明白的或者可以優化的地方,可以在下面評論。

wxss代碼:.contai position: relative;
}
.item_box{
z-index: 9;
}
.item{
width: 200rpx;
height: 320rpx;
background: #FFFFFF;
box-shadow: 0 2rpx 20rpx rgba(208, 213, 221, 0.5);
border-radius: 10px;
margin-bottom: 32rpx;
}
.item_name{
width: auto;
margin: 32rpx 32rpx 0;
font-size: 44rpx;
font-weight: bold;
line-height: 60rpx;
color: #333333;
}

.layer_box{
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.layer_item{
position: absolute;
transition: left 1s,top 1s;
}
.item_show{
opacity: 1;
}
.item_hide{
opacity: 0;
}






目前已知的bug:
當不停地快速拖動時,滑塊位置會閃爍不定,大概是因為拖動完成后,樣式還未完全渲染完畢,就開始了下一次拖動,導致滑塊的index計算錯誤。
如果增大小程序movable-view的damping屬性(默認值為20),該情況會有所改善,但滑動的動畫效果不夠美觀。
除此之外沒有找到合適的方法改善該情況,有大佬有思路可以在評論區留言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM