前言
vue1.0版本和2.0版本的過渡系統改變還是蠻徹底的,具體請自行詳看文件介紹:https://vuefe.cn/v2/guide/migration.html#過渡。在使用2.0版本做過渡效果的時候,雖然比1.0版本強大很多,但是實踐過程中還是踩了一些不應該踩但是還是踩了的坑。雖然官網文件已經很詳細地介紹了各種應用場景,但是這裡還是通過幾個小demo案例來感受下vue2.0 transition 帶來的便利吧!
實踐
這裡將通過四個實踐小案例來體驗和學習css過渡,css動畫,javascript鉤子,列表過渡的應用。至於案例用到的知識點就請自行學習官網文件。
css過渡–實踐
先來看看demo效果:
這個案例其實很簡單,通過一個transition來觸發多個子元素的過渡效果,我們只需要定義元素對應的過渡效果就可以,其他事情vue會幫我們搞定,由此可以擴充套件出其他酷炫的過渡場景效果。先來看看這個簡單案例的程式碼實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
<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效果:
這個案例其實跟上面的demo差不多,不同之處在於過渡效果是使用css動畫來實現,看下實現的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
<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:
這個一個非常low的模擬炮彈發射的場景,可以看到小球有拋物線軌跡運動的過渡,而且發射出去就不會再回來了,那麼這個demo就是使用了JavaScript鉤子結合css來實現的,接下來看下關鍵程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<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鉤子怎麼實現這個過渡:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
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效果:
其實就是個簡單的todo lists的小demo,可以看到,當其中一個元素過渡的時候,會影響其他元素的過渡。當然,刪除按鈕其實本身也是一個transition過渡,也就是說可以在transition-group裡使用transition,看下相關程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<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分支是支援的,只需在專案下安裝它即可:
1 |
sudo npm install --save git://github.com/vuejs/vue-touch.git#next |
這裡看下主要的樣式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
.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: 如需轉載,請註明出處,謝謝!!!