175 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// 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()
 | 
						||
		}
 | 
						||
	}
 | 
						||
}
 |