|
|
// nvue操作dom的库,用于获取dom的尺寸信息
|
|
|
const dom = uni.requireNativePlugin('dom');
|
|
|
const bindingX = uni.requireNativePlugin('bindingx');
|
|
|
const animation = uni.requireNativePlugin('animation');
|
|
|
import { getPx, getDuration } from '../../libs/function/index';
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
// 所有按钮的总宽度
|
|
|
buttonsWidth: 0,
|
|
|
// 是否正在移动中
|
|
|
moving: false
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
// 获取过渡时间
|
|
|
getDuratin() {
|
|
|
let duration = String(this.duration)
|
|
|
// 如果ms为单位,返回ms的数值部分
|
|
|
if (duration.indexOf('ms') >= 0) return parseInt(duration)
|
|
|
// 如果s为单位,为了得到ms的数值,需要乘以1000
|
|
|
if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
|
|
|
// 如果值传了数值,且小于30,认为是s单位
|
|
|
duration = Number(duration)
|
|
|
return duration < 30 ? duration * 1000 : duration
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
show(n) {
|
|
|
if(n) {
|
|
|
this.moveCellByAnimation('open')
|
|
|
} else {
|
|
|
this.moveCellByAnimation('close')
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
this.initialize()
|
|
|
},
|
|
|
methods: {
|
|
|
initialize() {
|
|
|
this.queryRect()
|
|
|
},
|
|
|
// 关闭单元格,用于打开一个,自动关闭其他单元格的场景
|
|
|
closeHandler() {
|
|
|
if(this.status === 'open') {
|
|
|
// 如果在打开状态下,进行点击的话,直接关闭单元格
|
|
|
return this.moveCellByAnimation('close') && this.unbindBindingX()
|
|
|
}
|
|
|
},
|
|
|
// 点击单元格
|
|
|
clickHandler() {
|
|
|
// 如果在移动中被点击,进行忽略
|
|
|
if(this.moving) return
|
|
|
// 尝试关闭其他打开的单元格
|
|
|
this.parent && this.parent.closeOther(this)
|
|
|
if(this.status === 'open') {
|
|
|
// 如果在打开状态下,进行点击的话,直接关闭单元格
|
|
|
return this.moveCellByAnimation('close') && this.unbindBindingX()
|
|
|
}
|
|
|
},
|
|
|
// 滑动单元格
|
|
|
onTouchstart(e) {
|
|
|
// 如果当前正在移动中,或者disabled状态,则返回
|
|
|
if(this.moving || this.disabled) {
|
|
|
return this.unbindBindingX()
|
|
|
}
|
|
|
if(this.status === 'open') {
|
|
|
// 如果在打开状态下,进行点击的话,直接关闭单元格
|
|
|
return this.moveCellByAnimation('close') && this.unbindBindingX()
|
|
|
}
|
|
|
// 特殊情况下,e可能不为一个对象
|
|
|
e?.stopPropagation && e.stopPropagation()
|
|
|
e?.preventDefault && e.preventDefault()
|
|
|
this.moving = true
|
|
|
// 获取元素ref
|
|
|
const content = this.getContentRef()
|
|
|
let expression = `min(max(${-this.buttonsWidth}, x), 0)`
|
|
|
// 尝试关闭其他打开的单元格
|
|
|
this.parent && this.parent.closeOther(this)
|
|
|
|
|
|
// 阿里为了KPI而开源的BindingX
|
|
|
this.panEvent = bindingX.bind({
|
|
|
anchor: content,
|
|
|
eventType: 'pan',
|
|
|
props: [{
|
|
|
element: content,
|
|
|
// 绑定width属性,设置其宽度值
|
|
|
property: 'transform.translateX',
|
|
|
expression
|
|
|
}]
|
|
|
}, (res) => {
|
|
|
this.moving = false
|
|
|
if (res.state === 'end' || res.state === 'exit') {
|
|
|
const deltaX = res.deltaX
|
|
|
if(deltaX <= -this.buttonsWidth || deltaX >= 0) {
|
|
|
// 如果触摸滑动的过程中,大于单元格的总宽度,或者大于0,意味着已经动过滑动达到了打开或者关闭的状态
|
|
|
// 这里直接进行状态的标记
|
|
|
this.$nextTick(() => {
|
|
|
this.status = deltaX <= -this.buttonsWidth ? 'open' : 'close'
|
|
|
})
|
|
|
} else if(Math.abs(deltaX) > getPx(this.threshold)) {
|
|
|
// 在移动大于阈值、并且小于总按钮宽度时,进行自动打开或者关闭
|
|
|
// 移动距离大于0时,意味着需要关闭状态
|
|
|
if(Math.abs(deltaX) < this.buttonsWidth) {
|
|
|
this.moveCellByAnimation(deltaX > 0 ? 'close' : 'open')
|
|
|
}
|
|
|
} else {
|
|
|
// 在小于阈值时,进行关闭操作(如果在打开状态下,将不会执行bindingX)
|
|
|
this.moveCellByAnimation('close')
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
// 释放bindingX
|
|
|
unbindBindingX() {
|
|
|
// 释放上一次的资源
|
|
|
if (this?.panEvent?.token != 0) {
|
|
|
bindingX.unbind({
|
|
|
token: this.panEvent?.token,
|
|
|
// pan为手势事件
|
|
|
eventType: 'pan'
|
|
|
})
|
|
|
}
|
|
|
},
|
|
|
// 查询按钮节点信息
|
|
|
queryRect() {
|
|
|
// 历遍所有按钮数组,通过getRectByDom返回一个promise
|
|
|
const promiseAll = this.options.map((item, index) => {
|
|
|
return this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0])
|
|
|
})
|
|
|
// 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
|
|
|
Promise.all(promiseAll).then(sizes => {
|
|
|
this.buttons = sizes
|
|
|
// 计算所有按钮总宽度
|
|
|
this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
|
|
|
})
|
|
|
},
|
|
|
// 通过nvue的dom模块,查询节点信息
|
|
|
getRectByDom(ref) {
|
|
|
return new Promise(resolve => {
|
|
|
dom.getComponentRect(ref, res => {
|
|
|
resolve(res.size)
|
|
|
})
|
|
|
})
|
|
|
},
|
|
|
// 移动单元格到左边或者右边尽头
|
|
|
moveCellByAnimation(status = 'open') {
|
|
|
if(this.moving) return
|
|
|
// 标识当前状态
|
|
|
this.moveing = true
|
|
|
const content = this.getContentRef()
|
|
|
const x = status === 'open' ? -this.buttonsWidth : 0
|
|
|
animation.transition(content, {
|
|
|
styles: {
|
|
|
transform: `translateX(${x}px)`,
|
|
|
},
|
|
|
duration: getDuration(this.duration, false),
|
|
|
timingFunction: 'ease-in-out'
|
|
|
}, () => {
|
|
|
this.moving = false
|
|
|
this.status = status
|
|
|
this.unbindBindingX()
|
|
|
})
|
|
|
},
|
|
|
// 获取元素ref
|
|
|
getContentRef() {
|
|
|
return this.$refs['u-swipe-action-item__content'].ref
|
|
|
},
|
|
|
beforeUnmount() {
|
|
|
this.unbindBindingX()
|
|
|
}
|
|
|
}
|
|
|
}
|