版本一代码,简洁:
<template>
<view class='zhuti' @touchend='dragEnd'>
<movable-area class="movable-area" :style='"display:"+movableViewInfo.showClass+"; height:"+"100%"+";"'>
<movable-view class='row list-row movable-row' out-of-bounds='true' damping='99999' @change="viewsChang"
:style='"height:"+pageInfo.rowHeight+"px;"' direction="vertical" :y="movableViewInfo.y">
<view class='col1 content'>{{movableViewInfo.data.text}}</view>
<view class="col3">
<icon type='download' color='Gray' size='25' />
</view>
</movable-view>
</movable-area>
<scroll-view class="scroll-view_H" scroll-with-animation :scroll-y="isTrue">
<block v-for="(item,index) in optionList">
<view class='row list-row' v-show="action!=item.id" :style='"height: "+pageInfo.rowHeight+"px;"'>
<view class='col1 content'>{{item.text}}</view>
<view class="col3">
<icon type='download' color='Gray' size='25' :data-action="item.id" :data-index='index'
@touchstart='dragStart' @touchmove='dragMove' />
</view>
</view>
</block>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
optionList: [{
id: 1,
text: "段落1 内容1"
},
{
id: 2,
text: "段落2 内容1"
},
{
id: 3,
text: "段落3 内容1"
},
{
id: 4,
text: "段落4 内容1"
},
{
id: 5,
text: "段落5 内容1"
},
{
id: 6,
text: "段落6 内容1"
},
{
id: 7,
text: "段落7 内容1"
},
{
id: 8,
text: "段落8 内容1"
},
{
id: 9,
text: "段落9 内容1"
},
{
id: 10,
text: "段落10 内容1"
},
{
id: 11,
text: "段落11 内容1"
},
{
id: 12,
text: "段落12 内容1"
},
{
id: 13,
text: "段落13 内容1"
},
{
id: 14,
text: "段落14 内容1"
},
],
isTrue: true,
isSorting: false,
movableViewInfo: {
y: 0,
showClass: 'none', //'none' inline,
data: {}
},
pageInfo: {
rowHeight: 62,
startIndex: null,
scrollY: true,
readyPlaceIndex: null,
startY: 0,
selectedIndex: null,
},
mainHeight: 0,
moveH: 0,
moveTop: 0,
timer: null,
outTime: null,
heiBh: 0,
//action是为了列表中隐藏当前拖拽的内容
action: null
}
},
onLoad() {
const query = uni.createSelectorQuery().in(this);
query.select('.zhuti').boundingClientRect(data => {
this.mainHeight = data.height
}).exec();
},
methods: {
viewsChang(e) {
this.heiBh = e.detail.y
//定时间是拦截速度的,可以不用
if (!this.timer) {
this.timer = setInterval(() => {
//1.7 和。1.3 是控制速度的,可以不用
if (this.heiBh + (this.pageInfo.rowHeight * 1.7) >= this.mainHeight) {
this.itemAdd(0)
} else if (this.heiBh <= this.pageInfo.rowHeight / 1.3) {
this.itemAdd(1)
}
}, 100)
}
},
itemAdd(v) {
this.isTrue = true
if (this.outTime) return
if (v == 0) {
if (!this.optionList[this.pageInfo.selectedIndex + 1]) return this.isTrue = false
this.outTime = setTimeout(() => {
var selectedData = this.optionList[this.pageInfo.selectedIndex]
this.optionList.splice(this.pageInfo.selectedIndex, 1)
this.pageInfo.selectedIndex++
this.optionList.splice(this.pageInfo.selectedIndex, 0, selectedData)
this.outTime = null
}, 100)
} else {
if (!this.optionList[this.pageInfo.selectedIndex + 1] || this.pageInfo.selectedIndex == 0)
return this.isTrue = false
this.outTime = setTimeout(() => {
var selectedData = this.optionList[this.pageInfo.selectedIndex]
this.optionList.splice(this.pageInfo.selectedIndex, 1)
this.pageInfo.selectedIndex--
this.optionList.splice(this.pageInfo.selectedIndex, 0, selectedData)
this.outTime = null
}, 100)
}
},
dragStart(event) {
this.isSorting = true
var startIndex = event.target.dataset.index
this.isTrue = false
this.action = event.target.dataset.action
// console.log('获取到的元素为', event, this.optionList[startIndex])
// return
// 初始化页面数据
this.pageInfo.startY = event.touches[0].clientY
this.pageInfo.readyPlaceIndex = startIndex
this.pageInfo.selectedIndex = startIndex
this.pageInfo.scrollY = false
this.pageInfo.startIndex = startIndex
this.movableViewInfo.y = this.pageInfo.startY - (this.pageInfo.rowHeight / 2)
// 初始化拖动控件数据
var movableViewInfo = this.movableViewInfo
movableViewInfo.data = this.optionList[startIndex]
movableViewInfo.showClass = "inline"
this.movableViewInfo = movableViewInfo
},
dragMove(event) {
// 计算拖拽距离
var movedDistance = event.touches[0].clientY - this.pageInfo.startY
this.movableViewInfo.y = this.pageInfo.startY - (this.pageInfo.rowHeight + 1 / 2) + movedDistance
// console.log('移动的距离为', movedDistance)
// 修改预计放置位置
var movedIndex = parseInt(movedDistance / this.pageInfo.rowHeight)
var readyPlaceIndex = this.pageInfo.startIndex + movedIndex
if (readyPlaceIndex < 0) {
readyPlaceIndex = 0
} else if (readyPlaceIndex >= this.optionList.length) {
readyPlaceIndex = this.optionList.length - 1
}
// console.log(event)
if (readyPlaceIndex != this.pageInfo.selectedIndex) {
var selectedData = this.optionList[this.pageInfo.selectedIndex]
this.optionList.splice(this.pageInfo.selectedIndex, 1)
this.optionList.splice(readyPlaceIndex, 0, selectedData)
// console.log('======optionList',readyPlaceIndex)
this.pageInfo.selectedIndex = readyPlaceIndex
}
// 移动movableView
this.pageInfo.readyPlaceIndex = readyPlaceIndex
// console.log('移动到了索引', readyPlaceIndex, '选项为', optionList[readyPlaceIndex])
},
dragEnd(event) {
this.isSorting = false
this.isTrue = true
this.action = null
// 重置页面数据
this.pageInfo.readyPlaceIndex = null
this.pageInfo.startY = null
this.pageInfo.selectedIndex = null
this.pageInfo.startIndex = null
this.pageInfo.scrollY = true
// 隐藏movableView
this.movableViewInfo.showClass = 'none'
clearInterval(this.timer)
this.timer = null
},
}
};
</script>
<style lang="scss">
page {
height: 100%;
width: 100%;
}
.scroll-view_H {
width: 100%;
height: 100%;
max-height: 100%;
}
.zhuti {
height: 100%;
width: 100%;
}
.row {
height: 47px;
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
}
.title-row {
border-bottom: 1px solid #888888;
color: #888888;
}
.list-row {
padding: 10px;
border-bottom: 1px solid #D9D9D9;
background-color: white;
box-sizing: border-box;
margin-bottom: 20px;
}
.movable-area {
position: absolute;
top: 0;
left: 0;
z-index: 10;
width: 100%;
// background: #F0AD4E;
}
.movable-row {
box-shadow: #D9D9D9 0px 0px 20px;
// background: #4CD964;
}
.col1 {
width: 60%;
}
.col3 {
width: 10%;
}
.content {
font-size: 17px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
- ios页面回弹
- 当拖拽时需要禁用滚动
(行为不锁页面,驱动页面也会跟着挪动。意思就是,你拖动item时,影响到父级也滑动了
)
就像这样,紫色是父会跟着挪动到红色这
此处我是在开始拖拽中改变了isSorting为true,然后在我们的html中,通过这个值,判断当前的class,如果isSorting为true,那么overflow-y:hidden。如果isSorting为false,那么overflow-y:auto。
你可以做处理,当页面拖动个数*当个高度大于页面高度时,触发滚动条位置+ltem的一个高度
后续再优化
- 对于pageInfo: {
rowHeight: 120,
scrollHeight: 120,
startIndex: null,
scrollY: true,
readyPlaceIndex: null,
startY: 0,
selectedIndex: null,
}中rowHeight: 120,
scrollHeight: 120,这两个高度,需要固定
而我实际使用中,我的项目item高度是不固定的,因此,我选择了一个最大的高度值,120。
- 增加拖拽事件触发的范围
在拖拽的同级,增加一个view,然后让它绝对定位,宽高为100%,并且级别比他同级高即可
<view class="sort_action" style="position: relative;">
<image src="../drag.png" class="sortIcon" style="z-index: 1;"></image>
<text class="sortText" style="z-index: 1;">长按拖动</text>
<view @touchstart='dragStart' @touchmove='dragMove' @touchend='dragEnd' :data-index='key' style="z-index: 2;position: absolute;top: 0;left: 0;width: 100%;height: 100%;">
</view>
</view>
- 拖拽时, 列表中隐藏当前拖拽的内容
看到另外一个组件:拖拽菜单 - DCloud 插件市场 有时间再继续研究