另外一種狀況和Vue過渡執行過程見初始渲染javascript
new Vue({ el: '#app-2', data: { taxiCalled: false } })
<style> .slideInRight { transform: translateX(300px); } .go { transition: all 2s ease-out; } </style> <div id="app-2"> <button @click="taxiCalled = true"> Call a cab </button> <transition enter-class="slideInRight" enter-active-class="go"> <p v-if="taxiCalled">?</p> </transition> </div>
這個過程到底發生了什麼?css
當點擊按鈕時,texiCalled
被設置成true
,而且taxi
插入頁面。實際上在完成這些動做以前,Vue讀取了指定的類enter-class
(這裏爲slideInRight
)並把它應用於taxi
的外包元素p
,而後指定類enter-active-class
的狀況是同樣的。vue
類enter-class
在第一幀以後就被移除了,類enter-active-class
也在動畫結束時被移除。java
這種建立動畫的方式成爲FLIPgit
taxi
在屏幕的右邊開始。taxi
在屏幕的最左邊做爲結束。transform
和opacity
在第一幀和最後幀兩個狀態之間扭轉,這裏使用translateX(300px)
使taxi
在兩個狀態間產生300px的位移。taxi
從右到左產生了300px的位移。new Vue({ el: '#app-3', data: { taxiCalled: false }, methods: { enter(el, done) { Velocity(el, { opacity: [1, 0], translateX: ["0px", "200px"] }, { duration: 2000, easing: "ease-out", complete: done }) }, leave(el, done) { Velocity(el, { opacity: [0, 1], 'font-size': ['0.1em', '1em'] }, { duration: 200, complete: done }) } } })
<div id="app-3"> <button @click="taxiCalled = true"> Call a cab </button> <button @click="taxiCalled = false"> Cancel </button> <transition @enter="enter" @leave="leave" :css="false"> <p v-if="taxiCalled">?</p> </transition> </div>
抓取enter
、leave
和appear
三種狀況的時間點,每種狀況上都定義了四種事件,總計12種事件(見API)。在這12種事件綁定鉤子函數,這些函數能夠配合CSS模式使用,也能夠單獨使用。這裏咱們在鉤子函數中使用velocity
腳本動畫引擎,單獨完成動畫配置。github
在這些鉤子中不必定非要使用
velocity
,可使用任何庫。
上面代碼中:css="false"
是告訴Vue,咱們關閉CSS的處理,這裏能夠節省點CPU時間,將跳過全部與CSS動畫相關代碼,單純的使用Javascript動畫。面試
咱們這裏分別在@enter
和@leave
上綁定了鉤子函數,他們將在taxi
被插入和離開時執行。函數的第一個參數爲外包標籤(這裏是<p>
),第二個參數done
必須寫,儘管你可能不去用它。這是由於用Javascript代替CSS,Vue沒法識別動畫何時完成,這裏Vue會認爲這個離開的動畫在@leave
事件以前就完成了,因此執行leave
沒有動畫渲染。segmentfault
關於velocity
,這種類型的API,咱們稱爲forcefeeding,咱們只要往它的實例中填寫數據,無論他內部如何運行。(具體查看velocity)api
在<transition>
使用appear
特性,在組件第一次被插入時執行相關的轉換。
使用它會給人帶來一種頁面很快的加載大量元素的感受(錯覺)。數組
這裏還使用了<transition>
的name
特性,可使用自定義的類名('自定義過渡的類名'的類似寫法),name-enter-active
、name-enter
等。
而使用這種自定義類名是一種好習慣。
<div id="app-4"> <transition appear name="flip"> <!-- 指定寬高由於是引用的圖片,不知道尺寸 --> <img src="https://b-ssl.duitang.com/uploads/item/201602/20/20160220213530_Z4reH.thumb.700_0.jpeg" style="width:300px;height:300px"> </transition> <p>在組件第一次插入文檔時執行相關的過渡</p> </div> <script> new Vue({ el: '#app-4' }) </script>
img { float: left; padding: 5px } .flip-enter-active { transition: all 5s cubic-bezier(0.55, 0.085, 0.68, 0.53); } .flip-enter { transform: scaleY(0) translateZ(0); opacity: 0; }
以上整個過程以下:
Vue發現appear
特性,開始查找<transition>
標籤中的JavaScript鉤子或指定的CSS類名。以後若是有一個name
被指定,就根據這個name
查到過渡的入口:mySpecifiedName-enter
、mySpecifiedName-enter-active
等。
若是以上過程失敗,就會找默認的v-enter
、v-enter-active
等。
new Vue({ el:'#app-5', data:{ kisses: 0 } })
#app-5 button span { color: rgb(255, 0, 140); } #app-5 p { margin: 0; position: absolute; font-size: 3em; } .fade-enter-active { transition: opacity 5s } .fade-leave-active { transition: opacity 5s; opacity: 0 } .fade-enter { opacity: 0 }
<div id="app-5"> <button @click="kisses++"> <span>?</span>Kiss!</button> <transition name="fade"> <p v-if="kisses < 3">? frog</p> <p v-if="kisses >= 3">? princess</p> </transition> </div>
結果發現切換後沒任何過渡效果,緣由是什麼呢?
原來是,Vue會啓動自身的優化系統,發現兩個元素如出一轍,就是內容不一樣,所以當切換時,只作了內容的替換,標籤<p></p>
部分並無被替換,所以沒有過渡的效果。
咱們能夠爲須要過渡的元素增長key
屬性,讓Vue識別青蛙與公主兩個不一樣的元素。以下:
<transition name="fade"> <p v-if="kisses < 1" key="frog">? frog</p> <p v-if="kisses >= 1" key="princess">? princess</p> </transition>
過渡效果正常了。
最佳實踐,在元素上使用
key
,尤爲是當元素間具備不一樣的語義時。
在有兩個以上元素時,你可能這麼作:
<transition name="fade"> <p v-if="kisses < 2" key="frog">? frog</p> <p v-else-if="kisses >= 2 && kisses <=5" key="princess">? princess</p> <p v-else key="santa">? santa</p> </transition>
更好的方法是咱們能夠根據已有的數據動態的處理多元素過渡。
new Vue({ el: '#app-6', data: { kisses: 0, kindOfTransformation: 'fade', transformationMode: 'in-out' }, computed: { transformation() { if (this.kisses < 3) { return 'frog' } if (this.kisses >= 3 && this.kisses <= 5) { return 'princess' } if (this.kisses > 5) { this.kindOfTransformation = 'zoom' this.transformationMode = 'out-in' return 'santa' } }, emoji() { switch (this.transformation) { case 'frog': return '?' case 'princess': return '?' case 'santa': return '?' } } } })
<div id="app-6"> <button @click="kisses++"><span>?</span>Kiss!</button> <transition :name="kindOfTransformation" :mode="transformationMode"> <p :key="transformation">{{emoji}} {{transformation}}</p> </transition> </div>
以上過渡的name
、mode
以及元素的key
和內容,都將根據實例數據與計算屬性進行動態的綁定。
這樣作更加的靈活,而且能夠爲不一樣的元素應用不一樣的過渡(name
和mode
的不一樣)。
這裏的mode
有三種狀況:
再來看一個過渡模式的例子。
new Vue({ el: '#app-7', data: { product: 0, products: ['?umbrella', '?computer', '?ball', '?camera'] } })
#app-7 { margin-left:300px; } #app-7 p { position: absolute; margin: 0; font-size: 3em; } .slide-enter-active { transition: transform .5s } .slide-leave-active { transition: transform .5s; transform: translateX(-300px); } .slide-enter { transform: translateX(300px) }
<div id="app-7" style="margin-bottom:100px;"> <button @click="product++">next</button> <transition name="slide" > <p :key="products[product % 4]">{{products[product % 4]}}</p> </transition> </div>
這彷佛沒什麼問題。那麼如今修改下CSS,去掉絕對定位。
#app-7 p { /* position: absolute; */ margin: 0; font-size: 3em; }
再來看看結果。
彷佛是有點不對勁,爲何會這樣呢?
看看過渡執行時的DOM,發現先後兩個元素的過渡是同時進行,這是Vue的默認狀況,即兩個<p></p>
同時存在,若是不使用絕對定位,那麼上一個就會把下一個的位置擠掉。
這下過渡模式mode
就派上用處了,咱們爲過渡添加mode
屬性爲out-in
,舊先出新後進。
<transition name="slide" mode="out-in"> <p :key="products[product % 4]">{{products[product % 4]}}</p> </transition>
列表過渡的狀況比較複雜。一個問題一個問題看吧。
當<transition>
中有多個並列的元素時,咱們又沒有使用v-if|else
指令做用其上時,會警告咱們使用<transition-group>
標籤代替它。
一組列表的過渡效果,由<transition-group>
包圍,有幾點比單元素過渡特殊的。
<transition-group>
上設置tag
屬性外包多個元素,如<transition-group tag="p">
v-for
把元素渲染成列表形式:key="data"
標記以與它的同胞們做區分進入/離開
,多了一個移動,使用name-move
來定義類名(後面詳細解釋)。看個普通示例。
new Vue({ el: '#app-1', data: { items: [1, 2, 3, 4, 5, 6, 7, 8, 9], nextNum: 10 }, methods: { ramdomIndex: function () { return Math.floor(Math.random() * this.items.length) }, add: function () { this.items.splice(this.ramdomIndex(), 0, this.nextNum++) }, remove: function () { this.items.splice(this.ramdomIndex(), 1) } } })
.num-list-item { margin-right: 10px; } .num-list-enter, .num-list-leave-to { opacity: 0; transform: translateY(30px); } .num-list-enter-active, .num-list-leave-active { transition: all 2s; }
<div id="app-1"> <button @click="add">添加</button> <button @click="remove">移除</button> <transition-group name="num-list" tag="p"> <span v-for="item in items" :key="item" class="num-list-item"> {{item}} </span> </transition-group> </div>
很明顯只有透明度的過渡,而Y軸(30px)的轉換過渡沒有成功,這是由於<span>
爲一個inline
元素,沒有這種轉換過渡的功能,所以咱們須要把它切換成inline-block
元素。
.num-list-item { /*把span切換成inline-block*/ display:inline-block; margin-right: 10px; }
仔細觀察下還能發現個問題,其餘元素由於進入/離開
的那個元素,會發生位置的變化,其餘元素這種移動變換沒有過渡的效果,這不是咱們想要的。
如今就輪到使用name-move
爲其餘元素在此時添加移動過渡效果,很簡單的作個修改:
.num-list-move,/* 爲其餘受到影響的元素添加移動過渡效果*/ .num-list-enter-active, .num-list-leave-active { transition: all 2s; }
觀察文檔結構,當進入/離開發生時,會給受影響的元素添加移動過渡類名num-list-move
。
進入過渡時沒問題了,其餘元素平滑的向右位移,可是離開過渡時其餘受影響元素的移動仍是沒有過渡效果,這是由於定位問題,離開的元素要有2秒消失,默認的static
定位要在2秒後離開元素纔會騰出空位讓給後面的元素,而此時此刻,移動過渡的時效2秒已通過去了,所以後面元素纔會很突兀的補位。
那麼,咱們能夠在離開狀態name-leave-active
上使用absolute
讓元素脫離正常的文檔流,那麼一發生離開,後面的元素就能夠開始正常的移動過渡了。以下修改:
/* 在此離開過渡的狀態類名添加absolute定位,以受影響元素正常的使用平滑過渡 */ .num-list-leave-active { position: absolute; }
總結下,就是當使用行內元素時,使用位置轉換的過渡須要把其設置爲inline-block
,不然位置轉換沒有效果。在離開/進入過渡時,受影響的其餘元素應該使用移動過渡name-move
爲期定義移動過渡。還須要在離開過渡狀態類中name-leave-active
設置離開過渡元素的定位爲absolute
使其脫離正常的文檔流,以不妨礙其餘元素的移動過渡。
觀察其上CSS代碼,能夠發現<span>
的進入/離開/移動過渡定義的過渡都同樣,即:
.num-list-move, .num-list-enter-active, .num-list-leave-active { transition: all 2s; }
所以能夠去掉這些過渡狀態類名,之間寫在<span>
的樣式裏就能夠了。
.num-list-item { display:inline-block; margin-right: 10px; /* 代替 進入/離開/移動過渡狀態類 */ transition: all 2s; } .num-list-enter, .num-list-leave-to { opacity: 0; transform: translateY(30px); } /* .num-list-move, .num-list-enter-active, .num-list-leave-active { transition: all 2s; } */ /* 在此離開過渡的狀態類名添加absolute定位,以受影響元素正常的使用平滑過渡 */ .num-list-leave-active { position: absolute; }
其餘都原封不動,效果和第一種同樣。
在第一種寫法的基礎上添加如下功能,再次理解移動過渡。
js中導入lodash庫,使用shuffle
方法從新排列數組items
。
methods: { ramdomIndex: function () { return Math.floor(Math.random() * this.items.length) }, add: function () { this.items.splice(this.ramdomIndex(), 0, this.nextNum++) }, remove: function () { this.items.splice(this.ramdomIndex(), 1) }, //使用`shuffle`方法從新排列數組`items`。 shuffle: function () { this.items = _.shuffle(this.items) } }
頁面添加一個按鈕
<div id="app-1"> <button @click="add">添加</button> <button @click="remove">移除</button> <button @click="shuffle">重排列</button> <transition-group name="num-list" tag="p"> <span v-for="item in items" :key="item" class="num-list -item"> {{item}} </span> </transition-group> </div>
一個汽車站:
new Vue({ el: '#app-2', data: { buses: [1, 2, 3, 4, 5], nextBus: 6 }, mounted() { setInterval(() => { const headOrTail = () => Math.random() >= 0.5 if (headOrTail()) { this.buses.push(this.nextBus) this.nextBus += 1 } else { this.buses.splice(0, 1) } }, 2000) } })
.station-bus { display: inline-block; margin-left: 10px; font-size: 2em; } .station-enter { opacity: 0; transform: translateX(30px); } .station-leave-to { opacity: 0; transform: translateX(-30px); } .station-move, .station-enter-active, .station-leave-active { transition: all 2s; } .station-leave-active { position: absolute; }
<div id="app-2"> <h3>公交車站</h3> <transition-group tag="p" name="station"> <span v-for="bus in buses" :key="bus" class="station-bus">?</span> </transition-group> {{buses}} </div>
在元素插入時的鉤子上定義一個timer
,每隔兩秒一輛車進入或離開,爲它們設置進入/離開過渡,和其餘受影響車輛的移動過渡。
若是想要在咱們的站點的各處重用一種過渡,把它包裝進一個組件是個好方法。
要在站點上展現/隱藏一些縮略的文章,咱們能夠編寫一個過渡組件,而後爲不一樣的縮略文章添加這個過渡組件使其擁有過渡效果。
Vue.component('puff', { functional: true, render: function (createElement, context) { var data = { props: { 'enter-active-class': 'magictime puffIn', 'leave-active-class': 'magictime puffOut' } } return createElement('transition', data, context.children) } }) new Vue({ el: '#app-3', data: { showRecipe: false, showNews: false } })
<link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/magic/1.1.0/magic.min.css"> <div id="app-3"> <button @click="showRecipe = !showRecipe"> Recipe </button> <button @click="showNews= !showNews"> Breaking News </button> <puff> <article v-if="showRecipe" class="card"> <h3> 過渡和動畫 </h3> <p> 自定義過渡的類名 在鉤子中使用Velocity 兩個元素之間的過渡 ... </p> </article> </puff> <puff> <article v-if="showNews" class="card"> <h3> 今日頭條 </h3> <p> 201七、2018面試分享(js面試題記錄)記得點贊分享哦;讓更多的人看到~~ </p> </article> </puff> </div>
這裏使用了一個magicCSS動畫庫和函數式組件(todo)。
定義一個全局的函數式組件。在其render
選項中定義函數並返回一個可重用的元素<puff>
,在內部經過magic將進入/離開的過渡效果添加到<puff>
的屬性上。在頁面須要的地方包裹該<puff>
元素便可。
響應是Vue永恆的主體,所以過渡和它的屬性均可以是動態的。這樣咱們能夠控制在特定的位置與時間使用特定的過渡。
在多個元素之間的過渡和過渡模式中咱們已經展現了動態過渡,對於不一樣的<p>
元素,咱們使用了不一樣的過渡效果和模式。