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.
174 lines
5.3 KiB
174 lines
5.3 KiB
// 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() |
|
} |
|
} |
|
}
|
|
|