vue2.0 transition -- demo實踐填坑


前言

vue1.0版本和2.0版本的過渡系統改變還是蠻徹底的,具體請自行詳看文檔介紹:https://vuefe.cn/v2/guide/migration.html#過渡。在使用2.0版本做過渡效果的時候,雖然比1.0版本強大很多,但是實踐過程中還是踩了一些不應該踩但是還是踩了的坑。雖然官網文檔已經很詳細地介紹了各種應用場景,但是這里還是通過幾個小demo案例來感受下vue2.0 transition 帶來的便利吧!

實踐

這里將通過四個實踐小案例來體驗和學習css過渡,css動畫,javascript鈎子,列表過渡的應用。至於案例用到的知識點就請自行學習官網文檔。
css過渡--實踐
先來看看demo效果:
css過濾

這個案例其實很簡單,通過一個transition來觸發多個子元素的過渡效果,我們只需要定義元素對應的過渡效果就可以,其他事情vue會幫我們搞定,由此可以擴展出其他酷炫的過渡場景效果。先來看看這個簡單案例的代碼實現:

<template>
	<div class="app">
		<button @click="showMenu" class="btn">{{text}}</button>
		<transition name="move">
			<div class="menu" v-show="show">
				<div class="inner inner-1">1</div>
				<div class="inner inner-2">2</div>
				<div class="inner inner-3">3</div>
			</div>
		</transition>
	</div>
</template>

<script type="text/ecmascript-6">
	export default {
		data () {
			return {
				show: false
			};
		},
		methods: {
			showMenu () {
				this.show = !this.show;
			}
		},
		computed: {
			text () {
				return this.show ? '收' : '開';
			}
		}
	};
</script>

<style lang="stylus" rel="stylesheet/stylus">
	.app
		.btn
			position: fixed
			bottom: 50px
			right: 10px
			z-index: 10
			width: 50px
			height: 50px
			line-height: 50px
			border-radius: 50%
			border: none
			outline: none
			color: #fff
			font-size: 18px
			background: blue
		.menu
			position: fixed
			bottom: 50px
			right: 10px
			width: 50px
			height: 50px
			border-radius: 50%
			transition: all .7s ease-in
			&.move-enter-active
				.inner
					transform: translate3d(0, 0, 0)
					transition-timing-function: cubic-bezier(0, .57, .44, 1.97)
				.inner-1
					transition-delay: .1s
				.inner-2
					transition-delay: .2s
				.inner-3
					transition-delay: .3s
			&.move-enter, &.move-leave-active
				.inner
					transition-timing-function: ease-in-out
				.inner-1
					transform: translate3d(0, 60px, 0)
					transition-delay: .3s
				.inner-2
					transform: translate3d(40px, 40px, 0)
					transition-delay: .2s
				.inner-3
					transform: translate3d(60px, 0, 0)
					transition-delay: .1s
			.inner
				display: inline-block
				position: absolute
				width: 30px
				height: 30px
				line-height: 30px
				border-radius: 50%
				background: red
				text-align: center
				color: #fff
				transition: all .4s
			.inner-1
				top: -50px
				left: 10px
			.inner-2
				left: -30px
				top: -30px
			.inner-3
				left: -50px
				top: 10px
</style>

可以看到我們的代碼基本主要是完成css過渡效果的樣式,而觸發過渡效果只是簡單地通過一個click事件就搞定了,vue會自動嗅探目標元素是否有 CSS 過渡或動畫,並在合適時添加/刪除 CSS 類名。那下一個demo就來簡單實現一下使用css animation 做過渡的效果。

css 動畫--實踐
先來看看demo效果:

css動畫

這個案例其實跟上面的demo差不多,不同之處在於過渡效果是使用css動畫來實現,看下實現的代碼:

<template>
	<div class="app">
	<button @click="showball" class="btn">show</button>
		<transition name="move" type="animation">
			<div class="ball" v-show="show">
				<div class="inner"></div>
			</div>
		</transition>
	</div>
</template>

<script type="text/ecmascript-6">
	export default {
		data () {
			return {
				show: false
			};
		},
		methods: {
			showball () {
				this.show = !this.show;
			}
		}
	};
</script>

<style lang="stylus" rel="stylesheet/stylus">
	@keyframes shape-change {
		0%, 100% {
			border-radius: 50%
			background: red
		}
		50% {
			border-radius: 0
			background: blue
		}
	}
	
	@keyframes moveball-in {
		0% {
			transform: translate3d(300px,-200px,0)
		}
		50% {
			transform: translate3d(100px,-400px,0)
		}
		100% {
			transform: translate3d(0,0,0)
		}
	}
	@keyframes moveball-out {
		0% {
			transform: translate3d(0,0,0)
		}
		50% {
			transform: translate3d(100px,-400px,0)
		}
		100% {
			transform: translate3d(300px,-200px,0)
		}
	}
	.app
		.btn
			width: 40px
			height: 30px
			margin-top: 40px
			border: none
			outline: none
			background: red
			color: #fff
		.ball
			position: absolute
			bottom: 20px
			left: 20px
			width: 50px
			height: 50px
			transition: all 1s cubic-bezier(.22,-0.86,.97,.58)
			&.move-enter-active
				opacity: 1
				animation: moveball-in 1s
				.inner
					animation: shape-change 1s
			&.move-leave-active
				opacity: 0.8
				animation: moveball-out 1s
				.inner
					animation: shape-change 1s
			.inner
				display: inline-block
				width: 30px
				height: 30px
				border-radius: 50%
				background: red
				transition: all 1s linear
</style>

從css代碼可以看出,我們只是在vue過渡類名下加了不同的animation而已。官網文檔明確說明當只使用transition或animation其中一種時,vue是能自動監聽對應的類型的,但是如果同一個元素同時使用兩種效果,就需要明確指定監聽哪一種類型,不過官網並沒有給出具體的栗子。那其實這個demo已經簡單地實現同時使用兩種類型的情況,可以看到有一個透明度的變化。但是假如animation里使用了transform,並且外面也使用了transform的話,那么元素在過渡的時候動畫效果就會有沖突,效果就有點出入了。

JavaScript鈎子 -- 實踐
前兩個栗子都是有進入和離開的過渡,但是如果一些場景只需要進入過渡然后就結束了,那么這時就可以使用JavaScript鈎子結合CSS transitions/animations來實現,當然也可以單獨使用。看下demo:

javascript鈎子

這個一個非常low的模擬炮彈發射的場景,可以看到小球有拋物線軌跡運動的過渡,而且發射出去就不會再回來了,那么這個demo就是使用了JavaScript鈎子結合css來實現的,接下來看下關鍵代碼:

<template>
	<div class="app">
		<div class="gun" @click="launch($event)"></div>
		<div class="shells-wrapper">
		  <transition v-for="shell in shells" name="launch-shell" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
		    <div class="shell" v-show="shell.show">
		      <div class="inner"></div>
		    </div>
		  </transition>
		</div>
		<div class="goal"></div>
	</div>
</template>

首先,由於本身這個demo是一組元素的過渡,所以有些童鞋就會覺得用2.0提供的transition-group不就行了嘛。不過transition-group是列表過渡,我的理解是那一組元素是相關聯的、互相影響的,但是這個demo的元素每個都是獨立的,只不過是一組獨立的元素過渡,所以還是用transition比較合理,那使用v-for就可以實現一組相同過渡的元素啦。接下來看JavaScript鈎子怎么實現這個過渡:

export default {
		data () {
			return {
				shells: [
					{
						show: false
					},
					{
						show: false
					},
					{
						show: false
					}
				]
			};
		},
		methods: {
			launch (event) {
				for (let i = 0; i < this.shells.length; i++) {
				  let shell = this.shells[i];
				  if (!shell.show) {
				    shell.show = true;
				    shell.target = event.target;
				    return;
				  }
				}
			},
			beforeEnter (el) {
				let count = this.shells.length;
				while (count--) {
					let shell = this.shells[count];
					if (shell.show) {
						let rect = shell.target.getBoundingClientRect();
						let left = rect.left - 32;
						let top = -(window.innerHeight - rect.top - 15);
						el.style.display = '';
						el.style.webkitTransform = `translate3d(0,${top}px,0)`;
						el.style.transform = `translate3d(0,${top}px,0)`;
						let inner = el.getElementsByClassName('inner')[0];
						inner.style.webkitTransform = `translate3d(${left}px,0,0)`;
						inner.style.transform = `translate3d(${left}px,0,0)`;
					}
				}
			},
			enter (el, done) {
				/* eslint-disable no-unused-vars */
				let refresh = el.offsetHeight;
				this.$nextTick(() => {
					el.style.webkitTransform = 'translate3d(0,0,0)';
					el.style.transform = 'translate3d(0,0,0)';
					let inner = el.getElementsByClassName('inner')[0];
					inner.style.webkitTransform = 'translate3d(0,0,0)';
					inner.style.transform = 'translate3d(0,0,0)';
				});
				done();
			},
			afterEnter (el) {
				let ball = this.shells[0];
				ball.show = false;
				el.style.display = 'none';
			}
		}
	};

過渡元素就不需要為其添加vue的過渡css類名了,只需在元素本身添加transition即可,那vue在之前css過渡的時候會自動幫我們去添加對應的類名來完成過渡效果,但是用javascript鈎子就需要我們自己完成這個始末狀態的設置了。當我們點擊觸發一個過渡的時候,我們在beforeEnter里先拿到當前元素的偏移位置,然后給過渡元素設置其起始位置,在enter里需要重新觸發下瀏覽器的重繪,然后在下一幀重新設置元素的結束位置,這時就會產生過渡效果,在過渡完成后我們將當前元素隱藏即可。那剛才講到的列表過渡,接下來就是關於使用transition-group的一個小demo了。

transition-group -- 實踐
先看下demo效果:

transition-group.gif

其實就是個簡單的todo lists的小demo,可以看到,當其中一個元素過渡的時候,會影響其他元素的過渡。當然,刪除按鈕其實本身也是一個transition過渡,也就是說可以在transition-group里使用transition,看下相關代碼:

<template>
	<div class="app">
		<button @click="add" class="add-btn">+</button>
		<transition-group name="slide" tag="ul" class="list-wrapper">
			<li class="list" v-for="(item, index) in lists" v-touch:swipeleft="showBtn.bind(this, index)" v-touch:swiperight="hideBtn.bind(this, index)" :key="item">
				<span class="text">{{item.text}}</span>
				<transition name="move">
					<button class="del-btn" @click="delList(index)" v-show="item.show">刪除</button>
				</transition>
			</li>
		</transition-group>
	</div>
</template>

有個小坑的地方就是,之前看官網列表過渡的栗子,它是一個數組,元素都是數字,並且每一項都必須設置唯一的key值。所以我完成demo的時候就自作聰明地將索引值傳給key,結果過渡老是不對,后來換成對應的item就正常了(生無可戀臉)。這個demo用到了vue-touch,雖然github上說不支持2.0版本了,但是有一個next分支是支持的,只需在項目下安裝它即可:

sudo npm install --save git://github.com/vuejs/vue-touch.git#next

這里看下主要的樣式:

.list
	display: flex
	width: 100%
	height: 40px
	line-height: 40px
	margin-bottom: 10px
	color: #666
	font-size: 14px
	background: #eee
	transition: all .4s
	&.slide-move
        transition: transform 1s
	&.slide-enter
		transform: translate3d(-100%, 0, 0)
	&.slide-leave-active
		position: absolute
		transform: translate3d(-100%, 0, 0)
	&:last-child
		margin-bottom: 0
	.del-btn
		flex: 0 0 60px
		border: none
		outline: none
		color: #fff
		background: red
		transition: all .4s
		&.move-enter, &.move-leave-active
			transform: translate3d(70px, 0, 0)
	.text
		flex: 1
		padding-left: 20px

如果改變定位過渡的duration與進入離開一樣的話,其實可以不用-move,這里設置-move的過渡的duration不同於元素進入離開的duration產生一種速度差,看起來舒服點。而且-leave-active需要設置position: absolute才會有效果。現在看來其實列表過渡也是很容易實現的。

最后

其實vue2.0過渡系統還提供了其他場景的過渡應用,這里就不展開贅述了。引用官網文檔的一句話:唯一的限制是你的想象力。哈哈...

PS: 如需轉載,請注明出處,謝謝!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM