transition.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // 定义一个一定时间后自动成功的promise,让调用nextTick方法处,进入下一个then方法
  2. const nextTick = () => new Promise(resolve => setTimeout(resolve, 1000 / 50))
  3. // nvue动画模块实现细节抽离在外部文件
  4. import animationMap from './nvue.ani-map.js'
  5. // #ifndef APP-NVUE
  6. // 定义类名,通过给元素动态切换类名,赋予元素一定的css动画样式
  7. const getClassNames = (name) => ({
  8. enter: `u-${name}-enter u-${name}-enter-active`,
  9. 'enter-to': `u-${name}-enter-to u-${name}-enter-active`,
  10. leave: `u-${name}-leave u-${name}-leave-active`,
  11. 'leave-to': `u-${name}-leave-to u-${name}-leave-active`
  12. })
  13. // #endif
  14. // #ifdef APP-NVUE
  15. // 引入nvue(weex)的animation动画模块,文档见:
  16. // https://weex.apache.org/zh/docs/modules/animation.html#transition
  17. const animation = uni.requireNativePlugin('animation')
  18. const getStyle = (name) => animationMap[name]
  19. // #endif
  20. export default {
  21. // #ifdef VUE3
  22. emits: ["beforeEnter", "enter", "afterEnter", "beforeLeave", "leave", "afterLeave", "click"],
  23. // #endif
  24. methods: {
  25. // 组件被点击发出事件
  26. clickHandler(e) {
  27. this.$emit('click', e)
  28. },
  29. // #ifndef APP-NVUE
  30. // vue版本的组件进场处理
  31. vueEnter() {
  32. // 动画进入时的类名
  33. const classNames = getClassNames(this.mode)
  34. // 定义状态和发出动画进入前事件
  35. this.status = 'enter'
  36. this.$emit('beforeEnter')
  37. this.inited = true
  38. this.display = true
  39. this.classes = classNames.enter
  40. this.$nextTick(async () => {
  41. await uni.$u.sleep(20)
  42. // 标识动画尚未结束
  43. this.$emit('enter')
  44. this.transitionEnded = false
  45. // 组件动画进入后触发的事件
  46. this.$emit('afterEnter')
  47. // 赋予组件enter-to类名
  48. this.classes = classNames['enter-to']
  49. })
  50. },
  51. // 动画离场处理
  52. vueLeave() {
  53. // 如果不是展示状态,无需执行逻辑
  54. if (!this.display) return
  55. const classNames = getClassNames(this.mode)
  56. // 标记离开状态和发出事件
  57. this.status = 'leave'
  58. this.$emit('beforeLeave')
  59. // 获得类名
  60. this.classes = classNames.leave
  61. this.$nextTick(() => {
  62. // 动画正在离场的状态
  63. this.transitionEnded = false
  64. this.$emit('leave')
  65. // 组件执行动画,到了执行的执行时间后,执行一些额外处理
  66. setTimeout(this.onTransitionEnd, this.duration)
  67. this.classes = classNames['leave-to']
  68. })
  69. },
  70. // #endif
  71. // #ifdef APP-NVUE
  72. // nvue版本动画进场
  73. nvueEnter() {
  74. // 获得样式的名称
  75. const currentStyle = getStyle(this.mode)
  76. // 组件动画状态和发出事件
  77. this.status = 'enter'
  78. this.$emit('beforeEnter')
  79. // 展示生成组件元素
  80. this.inited = true
  81. this.display = true
  82. // 在nvue安卓上,由于渲染速度慢,在弹窗,键盘,日历等组件中,渲染其中的内容需要时间
  83. // 导致出现弹窗卡顿,这里让其一开始为透明状态,等一定时间渲染完成后,再让其隐藏起来,再让其按正常逻辑出现
  84. this.viewStyle = {
  85. opacity: 0
  86. }
  87. // 等待弹窗内容渲染完成
  88. this.$nextTick(() => {
  89. // 合并样式
  90. this.viewStyle = currentStyle.enter
  91. Promise.resolve()
  92. .then(nextTick)
  93. .then(() => {
  94. // 组件开始进入前的事件
  95. this.$emit('enter')
  96. // nvue的transition动画模块需要通过ref调用组件,注意此处的ref不同于vue的this.$refs['u-transition']用法
  97. animation.transition(this.$refs['u-transition'].ref, {
  98. styles: currentStyle['enter-to'],
  99. duration: this.duration,
  100. timingFunction: this.timingFunction,
  101. needLayout: false,
  102. delay: 0
  103. }, () => {
  104. // 动画执行完毕,发出事件
  105. this.$emit('afterEnter')
  106. })
  107. })
  108. .catch(() => {})
  109. })
  110. },
  111. nvueLeave() {
  112. if (!this.display) {
  113. return
  114. }
  115. const currentStyle = getStyle(this.mode)
  116. // 定义状态和事件
  117. this.status = 'leave'
  118. this.$emit('beforeLeave')
  119. // 合并样式
  120. this.viewStyle = currentStyle.leave
  121. // 放到promise中处理执行过程
  122. Promise.resolve()
  123. .then(nextTick) // 等待几十ms
  124. .then(() => {
  125. this.transitionEnded = false
  126. // 动画正在离场的状态
  127. this.$emit('leave')
  128. animation.transition(this.$refs['u-transition'].ref, {
  129. styles: currentStyle['leave-to'],
  130. duration: this.duration,
  131. timingFunction: this.timingFunction,
  132. needLayout: false,
  133. delay: 0
  134. }, () => {
  135. this.onTransitionEnd()
  136. })
  137. })
  138. .catch(() => {})
  139. },
  140. // #endif
  141. // 完成过渡后触发
  142. onTransitionEnd() {
  143. // 如果已经是结束的状态,无需再处理
  144. if (this.transitionEnded) return
  145. this.transitionEnded = true
  146. // 发出组件动画执行后的事件
  147. this.$emit(this.status === 'leave' ? 'afterLeave' : 'afterEnter')
  148. if (!this.show && this.display) {
  149. this.display = false
  150. this.inited = false
  151. }
  152. }
  153. }
  154. }