li-guang
2021-01-06 b1ad3da0513c1e3e9d0e4ed294cd7a68aa65102f
hive-app/components/uni-transition/uni-transition.vue
New file
@@ -0,0 +1,279 @@
<template>
   <view v-if="isShow" ref="ani" class="uni-transition" :class="[ani.in]" :style="'transform:' +transform+';'+stylesObject"
    @click="change">
       <slot></slot>
   </view>
</template>
<script>
   // #ifdef APP-NVUE
   const animation = uni.requireNativePlugin('animation');
   // #endif
   /**
    * Transition 过渡动画
    * @description 简单过渡动画组件
    * @tutorial https://ext.dcloud.net.cn/plugin?id=985
    * @property {Boolean} show = [false|true] 控制组件显示或隐藏
     * @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
     *  @value fade 渐隐渐出过渡
     *  @value slide-top 由上至下过渡
     *  @value slide-right 由右至左过渡
     *  @value slide-bottom 由下至上过渡
     *  @value slide-left 由左至右过渡
     *  @value zoom-in 由小到大过渡
     *  @value zoom-out 由大到小过渡
    * @property {Number} duration 过渡动画持续时间
    * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
    */
   export default {
      name: 'uniTransition',
      props: {
         show: {
            type: Boolean,
            default: false
         },
         modeClass: {
            type: Array,
            default () {
               return []
            }
         },
         duration: {
            type: Number,
            default: 300
         },
         styles: {
            type: Object,
            default () {
               return {}
            }
         }
      },
      data() {
         return {
            isShow: false,
            transform: '',
            ani: { in: '',
               active: ''
            }
         };
      },
      watch: {
         show: {
            handler(newVal) {
               if (newVal) {
                  this.open()
               } else {
                  this.close()
               }
            },
            immediate: true
         }
      },
      computed: {
         stylesObject() {
            let styles = {
               ...this.styles,
               'transition-duration': this.duration / 1000 + 's'
            }
            let transfrom = ''
            for (let i in styles) {
               let line = this.toLine(i)
               transfrom += line + ':' + styles[i] + ';'
            }
            return transfrom
         }
      },
      created() {
         // this.timer = null
         // this.nextTick = (time = 50) => new Promise(resolve => {
         //    clearTimeout(this.timer)
         //    this.timer = setTimeout(resolve, time)
         //    return this.timer
         // });
      },
      methods: {
         change() {
            this.$emit('click', {
               detail: this.isShow
            })
         },
         open() {
            clearTimeout(this.timer)
            this.isShow = true
            this.transform = ''
            this.ani.in = ''
            for (let i in this.getTranfrom(false)) {
               if (i === 'opacity') {
                  this.ani.in = 'fade-in'
               } else {
                  this.transform += `${this.getTranfrom(false)[i]} `
               }
            }
            this.$nextTick(() => {
               setTimeout(() => {
                  this._animation(true)
               }, 50)
            })
         },
         close(type) {
            clearTimeout(this.timer)
            this._animation(false)
         },
         _animation(type) {
            let styles = this.getTranfrom(type)
            // #ifdef APP-NVUE
            if(!this.$refs['ani']) return
            animation.transition(this.$refs['ani'].ref, {
               styles,
               duration: this.duration, //ms
               timingFunction: 'ease',
               needLayout: false,
               delay: 0 //ms
            }, () => {
               if (!type) {
                  this.isShow = false
               }
               this.$emit('change', {
                  detail: this.isShow
               })
            })
            // #endif
            // #ifndef APP-NVUE
            this.transform = ''
            for (let i in styles) {
               if (i === 'opacity') {
                  this.ani.in = `fade-${type?'out':'in'}`
               } else {
                  this.transform += `${styles[i]} `
               }
            }
            this.timer = setTimeout(() => {
               if (!type) {
                  this.isShow = false
               }
               this.$emit('change', {
                  detail: this.isShow
               })
            }, this.duration)
            // #endif
         },
         getTranfrom(type) {
            let styles = {
               transform: ''
            }
            this.modeClass.forEach((mode) => {
               switch (mode) {
                  case 'fade':
                     styles.opacity = type ? 1 : 0
                     break;
                  case 'slide-top':
                     styles.transform += `translateY(${type?'0':'-100%'}) `
                     break;
                  case 'slide-right':
                     styles.transform += `translateX(${type?'0':'100%'}) `
                     break;
                  case 'slide-bottom':
                     styles.transform += `translateY(${type?'0':'100%'}) `
                     break;
                  case 'slide-left':
                     styles.transform += `translateX(${type?'0':'-100%'}) `
                     break;
                  case 'zoom-in':
                     styles.transform += `scale(${type?1:0.8}) `
                     break;
                  case 'zoom-out':
                     styles.transform += `scale(${type?1:1.2}) `
                     break;
               }
            })
            return styles
         },
         _modeClassArr(type) {
            let mode = this.modeClass
            if (typeof(mode) !== "string") {
               let modestr = ''
               mode.forEach((item) => {
                  modestr += (item + '-' + type + ',')
               })
               return modestr.substr(0, modestr.length - 1)
            } else {
               return mode + '-' + type
            }
         },
         // getEl(el) {
         //    console.log(el || el.ref || null);
         //    return el || el.ref || null
         // },
         toLine(name) {
            return name.replace(/([A-Z])/g, "-$1").toLowerCase();
         }
      }
   }
</script>
<style>
   .uni-transition {
      transition-timing-function: ease;
      transition-duration: 0.3s;
      transition-property: transform, opacity;
   }
   .fade-in {
      opacity: 0;
   }
   .fade-active {
      opacity: 1;
   }
   .slide-top-in {
      /* transition-property: transform, opacity; */
      transform: translateY(-100%);
   }
   .slide-top-active {
      transform: translateY(0);
      /* opacity: 1; */
   }
   .slide-right-in {
      transform: translateX(100%);
   }
   .slide-right-active {
      transform: translateX(0);
   }
   .slide-bottom-in {
      transform: translateY(100%);
   }
   .slide-bottom-active {
      transform: translateY(0);
   }
   .slide-left-in {
      transform: translateX(-100%);
   }
   .slide-left-active {
      transform: translateX(0);
      opacity: 1;
   }
   .zoom-in-in {
      transform: scale(0.8);
   }
   .zoom-out-active {
      transform: scale(1);
   }
   .zoom-out-in {
      transform: scale(1.2);
   }
</style>