实现效果
拖拽子组件代码
<template>
<ul class="m-drag" :style="{ height: itemHeight * newList.length + 'px' }">
<li
v-for="(item, index) in newList"
:key="index"
class="m-drag-item"
:class="{ active: currentIndex === index }"
:style="{
top: itemYList[index].top + 'px',
}"
>
<slot :item="item" />
<!-- css实现拖拽图标 -->
<div
class="icon"
@touchstart="touchStart($event, index)"
@touchmove="touchMove"
@touchend="touchEnd"
>
<i class="lines" />
</div>
</li>
</ul>
</template>
<script>
export default {
props: {
itemHeight: {
type: Number,
required: true,
},
list: {
type: Array,
required: true,
},
readonly: {
type: Boolean,
default: false,
},
},
data() {
return {
newList: [],
initialList: [],
initialItemYList: [],
itemYList: [],
touchY: 0,
currentItemY: {},
currentIndex: -1,
};
},
watch: {
list: {
handler(val) {
if (!val?.length) return;
this.newList = val;
this.initialList = [...val];
this.initialItemYList = this.getItemsY();
this.itemYList = this.getItemsY();
},
immediate: true,
},
},
created() {},
methods: {
getItemsY() {
return this.list.map((item, i) => {
return {
top: i * this.itemHeight,
};
});
},
touchStart(event, index) {
if (this.readonly) return;
this.h5BodyScroll(false);
const [{ pageY }] = event.touches;
this.currentIndex = index;
this.touchY = pageY;
this.currentItemY = this.itemYList[index];
},
touchMove(event) {
if (this.readonly) return;
const [{ pageY }] = event.touches;
const current = this.itemYList[this.currentIndex];
const prep = this.itemYList[this.currentIndex - 1];
const next = this.itemYList[this.currentIndex + 1];
this.itemYList[this.currentIndex] = {
top: current.top + (pageY - this.touchY),
};
this.touchY = pageY;
if (next && current.top > next.top - this.itemHeight / 2) {
this.changePosition(this.currentIndex + 1);
} else if (prep && current.top < prep.top + this.itemHeight / 2) {
this.changePosition(this.currentIndex - 1);
}
},
touchEnd() {
if (this.readonly) return;
this.change();
this.itemYList[this.currentIndex] =
this.initialItemYList[this.currentIndex];
this.initialList = [...this.newList];
this.currentIndex = -1;
this.h5BodyScroll(true);
},
changePosition(index) {
const tempItem = this.newList[this.currentIndex];
this.newList[this.currentIndex] = this.newList[index];
this.newList[index] = tempItem;
this.itemYList[index] = this.itemYList[this.currentIndex];
this.itemYList[this.currentIndex] = this.currentItemY;
this.currentIndex = index;
this.currentItemY = this.initialItemYList[this.currentIndex];
},
change() {
if (JSON.stringify(this.newList) == JSON.stringify(this.initialList))
return;
this.$emit("change", this.newList, this.newList[this.currentIndex]);
},
h5BodyScroll(flag) {
document.body.style.overflow = flag ? "initial" : "hidden";
},
},
};
</script>
<style scoped lang="scss">
.m-drag {
position: relative;
width: 100%;
overflow: auto;
::-webkit-scrollbar {
display: none;
}
.m-drag-item {
position: absolute;
left: 0;
right: 0;
transition: all ease 0.25s;
display: flex;
align-items: center;
font-size: 28rpx;
padding: 30rpx 0;
border-bottom: 1px solid #f5f5f5;
:last-child{
border-bottom: 0;
}
> :deep(*:not(.icon)) {
flex: 1;
}
.icon {
padding: 10rpx;
.lines {
background: #000;
width: 20px;
height: 1px;
border-radius: 100rpx;
margin-left: auto;
position: relative;
display: block;
transition: all ease 0.25s;
&::before,
&::after {
position: absolute;
width: inherit;
height: inherit;
border-radius: inherit;
background: #000;
transition: inherit;
content: "";
display: block;
}
&::before {
top: -14rpx;
}
&::after {
bottom: -14rpx;
}
}
}
&.active {
box-shadow: 0 0 14rpx rgba(0, 0, 0, 0.08);
transition: initial;
z-index: 1;
.icon .lines {
background: #2e97f9;
&::before,
&::after {
background: #2e97f9;
}
}
}
}
}
</style>
组件使用
<template>
<view class="deduction_list">
<Mdrag :item-height="50" :list="list" @change="dragComplete">
<template #default="{ item }">
<span class="name">{{ item.name }}</span>
</template>
</Mdrag>
</view>
</template>
<script>
import Mdrag from "./Mdrag.vue";
export default {
name: "sss",
components: { Mdrag },
data() {
return {
list: [
{
name: "支付宝",
},
{
name: "微信",
},
{
name: "银行卡",
},
],
};
},
mounted() {},
methods: {
dragComplete(newList, dragItem) {
console.log(newList, dragItem);
},
},
};
</script>
兼容ios-app-微信小程序 回弹问题
{
"path": "pages/deductionOrder/index",
"style": {
"navigationBarBackgroundColor":"#FFFFFF",
"navigationBarTitleText":"扣款顺序",
"disabledScroll": true,
"allowsBounceVertical": "NO",
"app-plus": {
"bounce": "none"
}
}
}