quanyawei
2023-10-20 d8b41fff43a2cee6a8f714ffa807623b15803786
uni_modules/uview-ui/components/u-swipe-action-item/nvue.js
New file
@@ -0,0 +1,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()
      }
   }
}