You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
5.3 KiB

7 months ago
// nvue操作dom的库,用于获取dom的尺寸信息
const dom = uni.requireNativePlugin('dom');
const bindingX = uni.requireNativePlugin('bindingx');
const animation = uni.requireNativePlugin('animation');
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) > uni.$u.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: uni.$u.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
},
beforeDestroy() {
this.unbindBindingX()
}
}
}