index.wxml:
<!--pages/index/index.wxml-->
<view class="index">
<!-- 数据展示区 -->
<scroll-view
class="scroll-list"
scroll-y
style="height: {{windowHeight - bottomHeight}}px"
bindscroll="bindscroll">
<view class="scroll-item" wx:for="{{optionsList}}" wx:key="index">
<view>{{item.time}}</view>
</view>
</scroll-view>
<!-- 页面可拖拽区域 -->
<view
wx:if='{{editting}}'
class="touchView"
catchtouchstart="scrollTouchStart"
catchtouchmove="scrollTouchMove"
catchtouchend="scrollTouchEnd"
style="height: {{windowHeight - bottomHeight}}px">
</view>
<!-- 页面拖拽处理区域 -->
<movable-area
wx:if='{{editting}}'
class="moveable_row"
style="height: {{windowHeight - bottomHeight}}px">
<movable-view
class="moveable_item {{moveData ? 'show' : 'hide'}}"
style="height: {{scrollItemHeight}}px;"
direction="vertical"
x="{{moveData.x}}"
y="{{moveData.y}}"
inertia="false"
damping="9999"
friction="9999"
>
<view class="scroll-item moving" hidden='{{!moveData}}'>
<view>{{selectItem.time}}</view>
</view>
</movable-view>
</movable-area>
<!-- 底部栏 -->
<view class="index-bottom">
<button type="primary" wx:if='{{!editting}}' bindtap="changeEditing">编辑</button>
<button type="warn" wx:else bindtap="changeEditing">取消</button>
</view>
</view>
index.js:
// pages/index/index.js
Page({
data: {
optionsList: [], // 数据源
windowHeight: 0, // 屏幕高度
scrollItemHeight: 0, // 列表单项高度
bottomHeight: 0, // 底部按钮高度
selectItem: {}, // 当前选中元素
selectIndex: 0, // 当前选中索引
editting: false, // 是否是“编辑”状态
moveData: null, // 列表项移动时记录移动位置
scrollTop: 0 // scroll-view距离顶部距离
},
onLoad: function () {
this.init()
},
// 初始化页面数据
init() {
this.setData({
optionsList: [{time: '12:00'}, {time: '13:00'}, {time: '14:00'}, {time: '15:00'}, {time: '16:00'}, {time: '17:00'}, {time: '18:00'}, {time: '19:00'}, {time: '20:00'}, {time: '21:00'}, {time: '22:00'}, {time: '23:00'}, {time: '24:00'}, {time: '25:00'}, {time: '26:00'}, {time: '27:00'}, {time: '28:00'}, {time: '29:00'}, {time: '30:00'}, {time: '31:00'}, {time: '32:00'}, {time: '33:00'}, {time: '34:00'}, {time: '35:00'}],
windowHeight: wx.getSystemInfoSync().windowHeight
}, () => {
wx.createSelectorQuery().select('.index-bottom').boundingClientRect(res => {
this.setData({
bottomHeight: res.height
})
}).exec()
wx.createSelectorQuery().select('.scroll-item').boundingClientRect(res => {
this.setData({
scrollItemHeight: res.height
})
}).exec()
});
},
// 开始拖拽
scrollTouchStart(event) {
const { scrollItemHeight } = this.data
const firstTouchPosition = {
x: event.changedTouches[0].pageX,
y: event.changedTouches[0].pageY,
}
const { data, index } = this.getPositionDomByXY(firstTouchPosition);
this.setData({
moveData:{
x: 0,
y: firstTouchPosition.y - scrollItemHeight / 2
},
selectItem: data,
selectIndex: index
})
},
// 拖拽ing...
scrollTouchMove(event) {
const { scrollItemHeight } = this.data
this.setData({
moveData:{
x: 0,
y: event.changedTouches[0].pageY - scrollItemHeight / 2
},
})
},
// 拖拽结束
scrollTouchEnd:function (event) {
const { selectIndex, optionsList } = this.data
const endTouchPosition = {
x: event.changedTouches[0].pageX,
y: event.changedTouches[0].pageY,
}
const { index } = this.getPositionDomByXY(endTouchPosition)
// 交换顺序
const temp = optionsList[selectIndex]
optionsList[selectIndex] = optionsList[index]
optionsList[index] = temp
this.setData({
optionsList,
moveData: null
})
},
// 根据(x,y)坐标轴获取页面元素
getPositionDomByXY(potions) {
const { scrollItemHeight, optionsList, scrollTop } = this.data
const y = potions.y + scrollTop;
const len = optionsList.length
for(let i = 0; i < len; i++){
if(y >= i*scrollItemHeight && y < (i+1)*scrollItemHeight){
// 返回匹配到的数据项
return {
data: optionsList[i],
index: i
};
}
}
return y > (len-1)*scrollItemHeight ? {
// 匹配项位于列表之下
data: optionsList[len - 1],
index: len - 1
} : {
// 匹配项位于列表之上
data: optionsList[0],
index: 0
}
},
// 切换编辑状态
changeEditing() {
const { editting } = this.data
this.setData({ editting: !editting })
},
// 监听滚动事件
bindscroll(e) {
this.data.scrollTop = e.detail.scrollTop
}
})
index.wxss:
/* pages/index/index.wxss */
.index {
position: relative;
}
/* 数据展示区 */
.scroll-list{
width: 100%;
}
.scroll-item {
width: 100%;
padding: 10rpx 20rpx;
box-sizing: border-box;
border: 1px solid #999;
}
.scroll-item.moving {
border: 1px dashed blue;
}
/* 页面可拖拽区域 */
.touchView {
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 1;
}
/* 页面拖拽处理区域 */
.moveable_row {
width: 100%;
position: absolute;
left: 0;
top: 0;
}
.moveable_item {
width: 100%;
}
.moveable_item.show {
background: #fff;
}
.moveable_item.hide {
background: transparent;
}
/* 底部栏 */
.index-bottom {
width: 100%;
display: flex;
justify-content: flex-end;
padding: 10rpx 20rpx;
box-sizing: border-box;
}
例子展示: