审查视图

node_modules/uview-ui/components/u-swipe-action-item/nvue.js 5.3 KB
韩昌 authored
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
// 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()
		}
	}
}