nvue.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // nvue操作dom的库,用于获取dom的尺寸信息
  2. const dom = uni.requireNativePlugin('dom');
  3. const bindingX = uni.requireNativePlugin('bindingx');
  4. const animation = uni.requireNativePlugin('animation');
  5. import { getDuration, getPx } from '@/uni_modules/uv-ui-tools/libs/function/index.js'
  6. export default {
  7. data() {
  8. return {
  9. // 所有按钮的总宽度
  10. buttonsWidth: 0,
  11. // 是否正在移动中
  12. moving: false
  13. }
  14. },
  15. computed: {
  16. // 获取过渡时间
  17. getDuratin() {
  18. let duration = String(this.duration)
  19. // 如果ms为单位,返回ms的数值部分
  20. if (duration.indexOf('ms') >= 0) return parseInt(duration)
  21. // 如果s为单位,为了得到ms的数值,需要乘以1000
  22. if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
  23. // 如果值传了数值,且小于30,认为是s单位
  24. duration = Number(duration)
  25. return duration < 30 ? duration * 1000 : duration
  26. }
  27. },
  28. watch: {
  29. show(n) {
  30. if(n) {
  31. this.moveCellByAnimation('open')
  32. } else {
  33. this.moveCellByAnimation('close')
  34. }
  35. }
  36. },
  37. mounted() {
  38. setTimeout(()=>{
  39. this.initialize()
  40. },20)
  41. },
  42. methods: {
  43. initialize() {
  44. this.queryRect()
  45. },
  46. // 关闭单元格,用于打开一个,自动关闭其他单元格的场景
  47. closeHandler() {
  48. if(this.status === 'open') {
  49. // 如果在打开状态下,进行点击的话,直接关闭单元格
  50. return this.moveCellByAnimation('close') && this.unbindBindingX()
  51. }
  52. },
  53. // 点击单元格
  54. clickHandler() {
  55. // 如果在移动中被点击,进行忽略
  56. if(this.moving) return
  57. // 尝试关闭其他打开的单元格
  58. this.parent && this.parent.closeOther(this)
  59. if(this.status === 'open') {
  60. // 如果在打开状态下,进行点击的话,直接关闭单元格
  61. return this.moveCellByAnimation('close') && this.unbindBindingX()
  62. }
  63. },
  64. // 滑动单元格
  65. onTouchstart(e) {
  66. // 如果当前正在移动中,或者disabled状态,则返回
  67. if(this.moving || this.disabled) {
  68. return this.unbindBindingX()
  69. }
  70. if(this.status === 'open') {
  71. // 如果在打开状态下,进行点击的话,直接关闭单元格
  72. return this.moveCellByAnimation('close') && this.unbindBindingX()
  73. }
  74. // 特殊情况下,e可能不为一个对象
  75. e?.stopPropagation && e.stopPropagation()
  76. e?.preventDefault && e.preventDefault()
  77. this.moving = true
  78. // 获取元素ref
  79. const content = this.getContentRef()
  80. let expression = `min(max(${-this.buttonsWidth}, x), 0)`
  81. // 尝试关闭其他打开的单元格
  82. this.parent && this.parent.closeOther(this)
  83. // 阿里为了KPI而开源的BindingX
  84. this.panEvent = bindingX.bind({
  85. anchor: content,
  86. eventType: 'pan',
  87. props: [{
  88. element: content,
  89. // 绑定width属性,设置其宽度值
  90. property: 'transform.translateX',
  91. expression
  92. }]
  93. }, (res) => {
  94. this.moving = false
  95. if (res.state === 'end' || res.state === 'exit') {
  96. const deltaX = res.deltaX
  97. if(deltaX <= -this.buttonsWidth || deltaX >= 0) {
  98. // 如果触摸滑动的过程中,大于单元格的总宽度,或者大于0,意味着已经动过滑动达到了打开或者关闭的状态
  99. // 这里直接进行状态的标记
  100. this.$nextTick(() => {
  101. this.status = deltaX <= -this.buttonsWidth ? 'open' : 'close'
  102. })
  103. } else if(Math.abs(deltaX) > getPx(this.threshold)) {
  104. // 在移动大于阈值、并且小于总按钮宽度时,进行自动打开或者关闭
  105. // 移动距离大于0时,意味着需要关闭状态
  106. if(Math.abs(deltaX) < this.buttonsWidth) {
  107. this.moveCellByAnimation(deltaX > 0 ? 'close' : 'open')
  108. }
  109. } else {
  110. // 在小于阈值时,进行关闭操作(如果在打开状态下,将不会执行bindingX)
  111. this.moveCellByAnimation('close')
  112. }
  113. }
  114. })
  115. },
  116. // 释放bindingX
  117. unbindBindingX() {
  118. // 释放上一次的资源
  119. if (this?.panEvent?.token != 0) {
  120. bindingX.unbind({
  121. token: this.panEvent?.token,
  122. // pan为手势事件
  123. eventType: 'pan'
  124. })
  125. }
  126. },
  127. // 查询按钮节点信息
  128. queryRect() {
  129. // 历遍所有按钮数组,通过getRectByDom返回一个promise
  130. const promiseAll = this.options.map(async(item, index) => {
  131. return await this.getRectByDom(this.$refs[`uv-swipe-action-item__right__button-${index}`][0])
  132. })
  133. // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
  134. Promise.all(promiseAll).then(sizes => {
  135. this.buttons = sizes
  136. // 计算所有按钮总宽度
  137. this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
  138. })
  139. },
  140. // 通过nvue的dom模块,查询节点信息
  141. getRectByDom(ref) {
  142. return new Promise(resolve => {
  143. dom.getComponentRect(ref, res => {
  144. resolve(res.size)
  145. })
  146. })
  147. },
  148. // 移动单元格到左边或者右边尽头
  149. moveCellByAnimation(status = 'open') {
  150. if(this.moving) return
  151. // 标识当前状态
  152. this.moveing = true
  153. const content = this.getContentRef()
  154. const x = status === 'open' ? -this.buttonsWidth : 0
  155. animation.transition(content, {
  156. styles: {
  157. transform: `translateX(${x}px)`,
  158. },
  159. duration: getDuration(this.duration, false),
  160. timingFunction: 'ease-in-out'
  161. }, () => {
  162. this.moving = false
  163. this.status = status
  164. this.unbindBindingX()
  165. })
  166. },
  167. // 获取元素ref
  168. getContentRef() {
  169. return this.$refs['uv-swipe-action-item__content'].ref
  170. }
  171. },
  172. // #ifdef VUE2
  173. beforeDestroy() {
  174. this.unbindBindingX()
  175. },
  176. // #endif
  177. // #ifdef VUE3
  178. unmounted() {
  179. this.unbindBindingX()
  180. }
  181. // #endif
  182. }