<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>