组件SlideDelete.vue
<template>
<div :class="componentName" v-slide="">
<div class="slide__top" ref="slideItem" :style="wrapperStyle">
<slot name="item" v-if="$slots.item" />
<slot v-else />
</div>
<div :class="`slide__del ${delCls}`" @click="$emit('del-click')">
<slot name="del" v-if="$slots.del" />
<span v-else>{{ delText }}</span>
</div>
</div>
</template>
<script>
const COMPONENT_NAME = 'slide-delete'
export default {
name: COMPONENT_NAME,
computed: {
wrapperStyle() {
return {
transform: `translate3d(${this.offset}px, 0, 0)`,
transition: this.draging ? 'none' : '.6s cubic-bezier(0.18, 0.89, 0.32, 1)'
}
}
},
data() {
return {
delAreaWidth: 70,
open: false,
offset: 0,
draging: false,
componentName: COMPONENT_NAME
}
},
props: {
threshold: {
type: Number,
default: 35
},
delCls: {
type: String,
default: 'slide__del-red'
},
delText: {
type: String,
default: '删除'
}
},
methods: {
getTouch(touch) {
return touch.changedTouches[0] || touch.targetTouches[0]
},
isAngleLeft(diffX, diffY) {
const x = Math.abs(diffX)
const y = Math.abs(diffY)
return !(x < 5 || (x >= 5 && y >= x * 1.73))
},
setOpen(type = false) {
this.open = type
this.offset = type ? -this.delAreaWidth : 0
},
closeOther() {
this.$parent.$children
.filter(vNode =>
vNode.$el.classList.contains(this.componentName) &&
vNode.open &&
vNode !== this
)
.forEach(vNode => {
vNode.setOpen(false)
})
}
},
directives: {
slide: {
bind(el, binding, vNode) {
let startX, startY, diffX, diffY
let vm = vNode.context
el.addEventListener('touchstart', (e)=> {
const { clientX, clientY } = vm.getTouch(e)
startX = clientX
startY = clientY
vm.draging = true
})
el.addEventListener('touchmove', (e)=> {
const { clientX, clientY } = vm.getTouch(e)
diffX = clientX - startX
diffY = clientY - startY
if (
vm.isAngleLeft(diffX, diffY) &&
Math.abs(diffX) <= vm.delAreaWidth &&
(
diffX < 0 && !vm.open ||
diffX > 0 && vm.open
)
) {
event.preventDefault()
vm.offset = vm.open ? diffX - vm.delAreaWidth : diffX
}
})
el.addEventListener('touchend', (e)=> {
const { clientX, clientY } = vm.getTouch(e)
diffX = clientX - startX
diffY = clientY - startY
vm.draging = false
if (
diffX > 0 && diffX > vm.threshold ||
diffX < 0 && diffX > -vm.threshold
) {
vm.open = false
vm.offset = 0
} else if (
vm.isAngleLeft(diffX, diffY) &&
(
diffX > 0 && diffX <= vm.threshold ||
diffX < 0 && diffX <= -vm.threshold
)
) {
vm.open = true
vm.offset = -vm.delAreaWidth
vm.closeOther.call(vm, el)
vm.$emit('slip-open', vm)
}
})
}
}
}
}
</script>
<style lang="scss">
.slide {
position: relative;
.slide__top {
position: relative;
width: 100%;
z-index: 1;
background-color: #fff;
}
.slide__del {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 70px;
color: #fff;
z-index: 0;
span {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
}
.slide__del-red {
background-color: #f23030;
}
}
</style>
使用:
<template>
<div id="app">
<div class="wrapper">
<div class="wrapper-item">
<slide-del
v-for="(item, i) in list"
:key="i"
ref="slipDel"
del-text="移除"
@del-click="del(item.id)"
@slip-open="slipOpen"
>
<div class="demo-item">{{item.title}}</div>
<!-- <div slot="del">删除icon可编辑</div> -->
</slide-del>
</div>
</div>
</div>
</template>
<script>
import SlideDel from '@/components/SlideDelete.vue'
export default {
name: 'app',
data() {
return {
list: [{title: 1, id: 1},{title: 2, id: 2},{title: 3, id: 3}],
}
},
methods: {
slipOpen(vm) {
},
//删除操作
del(id) {
console.log(id)
}
},
components: {
SlideDel
}
}
</script>
<style>
.demo-item {
padding: 20px 0;
border-bottom: 1px #ccc solid;
}
.wrapper {
position: absolute;
left: 0;
top: 0;
width: 100%;
bottom: 0;
overflow: auto;
}
</style>