mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-11-04 16:17:40 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			291 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<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 {}
 | 
						|
				}
 | 
						|
			},
 | 
						|
			maskBackgroundColor: {
 | 
						|
				type: String,
 | 
						|
				default: 'rgba(0, 0, 0, 0.4)'
 | 
						|
			}
 | 
						|
		},
 | 
						|
		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,
 | 
						|
					backgroundColor: this.maskBackgroundColor,
 | 
						|
					'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>
 |