# 多個 元素 / 元件 的轉場 >[竹白記事本](https://chupainotebook.bloggi.co/) [Vue Transition|目錄](https://hackmd.io/@chupai/HkQQA8c4U) ###### tags: `Vue Transition` `竹白的 Vue.js 學習筆記` ## 多個元素的轉場 `<transition>` 也可以用在多個元素的轉場。 舉例來說: ```htmlmixed <transition> <table v-if="items.length > 0"> <!-- ... --> </table> <p v-else>Sorry, no items found.</p> </transition> ``` 但要注意,Vue 在切換畫面時,並不會完全置換元素,會使用一種最大限度減少動態元素並且儘可能的嘗試修復/再利用相同類型元素的算法。 因此當有相同標籤的元素在做切換時,需要使用 `key` 特性設置唯一的值來標記以讓 Vue 區分它們。 ```htmlmixed <transition> <button v-if="isEditing" key="save"> Save </button> <button v-else key="edit"> Edit </button> </transition> ``` 在一些場景中,也可以通過給同一個元素的 key 特性設置不同的狀態來代替 `v-if` 和 `v-else`,上面的範例可以重寫為: ```htmlmixed <transition> <button v-bind:key="isEditing"> {{ isEditing ? 'Save' : 'Edit' }} </button> </transition> ``` 使用多個 `v-if` 的多個元素的轉場可以重寫為綁定了動態屬性的單個元素轉場。例如: ```htmlmixed <transition> <button v-if="docState === 'saved'" key="saved"> Edit </button> <button v-if="docState === 'edited'" key="edited"> Save </button> <button v-if="docState === 'editing'" key="editing"> Cancel </button> </transition> ``` 可以重寫為: ```htmlmixed <!-- .html --> <transition> <button :key="docState"> {{ buttonMessage }} </button> </transition> ``` ```javascript // ... computed: { buttonMessage: function () { switch (this.docState) { case 'saved': return 'Edit' case 'edited': return 'Save' case 'editing': return 'Cancel' } } } ``` ### 1. 轉場模式 請考慮以下程式碼: ```htmlmixed <transition> <button :key="isEditing" @click="isEditing = !isEditing"> {{ isEditing ? 'On' : 'Off' }} </button> </transition> ``` <iframe height="200" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 問題" src="https://codepen.io/CHUPAIWANG/embed/ZEYpreQ?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true"> See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/ZEYpreQ'>Vue CSS 轉場 轉場模式 問題</a> by CHUPAIWANG (<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> 在 on 按鈕和 off 按鈕的轉場中,兩個按鈕都被重繪了,一個離開轉場的時候另一個開始進入轉場。這是 `<transition>` 的預設行為,進入和離開同時發生。 你可能會想到,可以加上 `position: absolute`,因為兩者重疊了,所以不會產生元素位移的情況: ```css button { position: absolute; } ``` <iframe height="200" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 絕對定位" src="https://codepen.io/CHUPAIWANG/embed/zYxKRzo?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true"> See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/zYxKRzo'>Vue CSS 轉場 轉場模式 絕對定位</a> by CHUPAIWANG (<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> 但如果加上 `translate` 讓它們運動像滑動,還是會有兩個元素同時出現的破綻: ```css .v-enter, .v-leave-to { opacity: 0; transform: translateX(100%); } ``` <iframe height="200" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 絕對定位 + 滑動" src="https://codepen.io/CHUPAIWANG/embed/abzmqyW?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true"> See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/abzmqyW'>Vue CSS 轉場 轉場模式 絕對定位 + 滑動</a> by CHUPAIWANG (<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> 同時生效的進入和離開的轉場不能滿足所有要求,所以 Vue 提供了轉場模式: - `in-out`:新元素先進行轉場,完成之後當前元素轉場離開。 - `out-in`:當前元素先進行轉場,完成之後新元素轉場進入。 加上 `mode="out-in"` 的效果: ```htmlmixed <transition mode="out-in"> <button :key="isEditing" @click="isEditing = !isEditing"> {{ isEditing ? 'On' : 'Off' }} </button> </transition> ``` ```css .v-enter-active, .v-leave-active { transition: 1.5s opacity; } ``` <iframe height="265" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 out-in" src="https://codepen.io/CHUPAIWANG/embed/vYEXdWr?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true"> See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/vYEXdWr'>Vue CSS 轉場 轉場模式 out-in</a> by CHUPAIWANG (<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> `in-out` 模式的範例: ```css button { position: absolute; } .v-enter,.v-leave-to { opacity: 0; } .v-enter { transform: translateX(100%); } .v-leave-to { transform: translateX(-100%); } ``` <iframe height="265" style="width: 100%;" scrolling="no" title="Vue CSS 轉場 轉場模式 in-out" src="https://codepen.io/CHUPAIWANG/embed/OJPRvVJ?height=265&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true"> See the Pen <a href='https://codepen.io/CHUPAIWANG/pen/OJPRvVJ'>Vue CSS 轉場 轉場模式 in-out</a> by CHUPAIWANG (<a href='https://codepen.io/CHUPAIWANG'>@CHUPAIWANG</a>) on <a href='https://codepen.io'>CodePen</a>. </iframe> ## 多個元件的轉場 多個元件的轉場簡單很多,我們不需要使用 `key` 特性。只需要使用 **動態切換** 元件: ```htmlmixed <!-- .html --> <div id="app"> <input type="radio" value="v-a" v-model="view" />A <input type="radio" value="v-b" v-model="view" />B <transition name="component-fade" mode="out-in"> <component v-bind:is="view"></component> </transition> </div> ``` ```css .component-fade-enter, .component-fade-leave-to { opacity: 0; } .component-fade-enter-active, .component-fade-leave-active { transition: opacity 0.3s ease; } ``` ```javascript const vm = new Vue({ el: '#app', data: { view: 'v-a', }, components: { 'v-a': { template: '<div>Component A</div>', }, 'v-b': { template: '<div>Component B</div>', }, }, }); ```