-
5.2 TransitionGroup
-
<TransitionGroup> 是一个内置组件,用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。
和 <Transition> 的区别
<TransitionGroup> 支持和 <Transition> 基本相同的 props、CSS 过渡 class 和 JavaScript 钩子监听器,但有以下几点区别:
1.默认情况下,它不会渲染一个容器元素。但你可以通过传入 tag prop 来指定一个元素作为容器元素来渲染。
2.过渡模式在这里不可用,因为我们不再是在互斥的元素之间进行切换。
3.列表中的每个元素都必须有一个独一无二的 key attribute。
4.CSS 过渡 class 会被应用在列表内的元素上,而不是容器元素上。
TIP:当在 DOM 模板中使用时,组件名需要写为 <transition-group>。
进入 / 离开动画
这里是 <TransitionGroup> 对一个 v-for 列表添加进入 / 离开动画的示例:
<TransitionGroup name="list" tag="ul"> <li v-for="item in items" :key="item"> {{ item }} </li> </TransitionGroup>
.list-enter-active, .list-leave-active { transition: all 0.5s ease; } .list-enter-from, .list-leave-to { opacity: 0; transform: translateX(30px); }
移动动画
上面的示例有一些明显的缺陷:当某一项被插入或移除时,它周围的元素会立即发生“跳跃”而不是平稳地移动。我们可以通过添加一些额外的 CSS 规则来解决这个问题:
css
.list-move, /* 对移动中的元素应用的过渡 */ .list-enter-active, .list-leave-active { transition: all 0.5s ease; } .list-enter-from, .list-leave-to { opacity: 0; transform: translateX(30px); } /* 确保将离开的元素从布局流中删除 以便能够正确地计算移动的动画。 */ .list-leave-active { position: absolute; }
现在它看起来好多了,甚至对整个列表执行洗牌的动画也都非常流畅:
<!-- 通过内建的 <TransitionGroup> 实现“FLIP”列表过渡效果。 https://aerotwist.com/blog/flip-your-animations/ --> <script> import { shuffle } from 'lodash-es' const getInitialItems = () => [1, 2, 3, 4, 5] let id = getInitialItems().length + 1 export default { data() { return { items: getInitialItems() } }, methods: { insert() { const i = Math.round(Math.random() * this.items.length) this.items.splice(i, 0, id++) }, reset() { this.items = getInitialItems() }, shuffle() { this.items = shuffle(this.items) }, remove(item) { const i = this.items.indexOf(item) if (i > -1) { this.items.splice(i, 1) } } } } </script> <template> <button @click="insert">insert at random index</button> <button @click="reset">reset</button> <button @click="shuffle">shuffle</button> <TransitionGroup tag="ul" name="fade" class="container"> <div v-for="item in items" class="item" :key="item"> {{ item }} <button @click="remove(item)">x</button> </div> </TransitionGroup> </template> <style> .container { position: relative; padding: 0; } .item { width: 100%; height: 30px; background-color: #f3f3f3; border: 1px solid #666; box-sizing: border-box; } /* 1. 声明过渡效果 */ .fade-move, .fade-enter-active, .fade-leave-active { transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1); } /* 2. 声明进入和离开的状态 */ .fade-enter-from, .fade-leave-to { opacity: 0; transform: scaleY(0.01) translate(30px, 0); } /* 3. 确保离开的项目被移除出了布局流 以便正确地计算移动时的动画效果。 */ .fade-leave-active { position: absolute; } </style>
效果图:
通过在 JavaScript 钩子中读取元素的 data attribute,我们可以实现带渐进延迟的列表动画。首先,我们把每一个元素的索引渲染为该元素上的一个 data attribute:
<TransitionGroup tag="ul" :css="false" @before-enter="onBeforeEnter" @enter="onEnter" @leave="onLeave" > <li v-for="(item, index) in computedList" :key="item.msg" :data-index="index" > {{ item.msg }} </li> </TransitionGroup>
接着,在 JavaScript 钩子中,我们基于当前元素的 data attribute 对该元素的进场动画添加一个延迟。以下是一个基于 GreenSock library 的动画示例:
function onEnter(el, done) { gsap.to(el, { opacity: 1, height: '1.6em', delay: el.dataset.index * 0.15, onComplete: done }) }
完整实例:
<script> import gsap from 'gsap' const list = [ { msg: 'Bruce Lee' }, { msg: 'Jackie Chan' }, { msg: 'Chuck Norris' }, { msg: 'Jet Li' }, { msg: 'Kung Fury' } ] export default { data() { return { query: '' } }, computed: { computedList() { return list.filter((item) => item.msg.toLowerCase().includes(this.query)) } }, methods: { onBeforeEnter(el) { el.style.opacity = 0 el.style.height = 0 }, onEnter(el, done) { gsap.to(el, { opacity: 1, height: '1.6em', delay: el.dataset.index * 0.15, onComplete: done }) }, onLeave(el, done) { gsap.to(el, { opacity: 0, height: 0, delay: el.dataset.index * 0.15, onComplete: done }) } } } </script> <template> <input v-model="query" /> <TransitionGroup tag="ul" :css="false" @before-enter="onBeforeEnter" @enter="onEnter" @leave="onLeave" > <li v-for="(item, index) in computedList" :key="item.msg" :data-index="index" > {{ item.msg }} </li> </TransitionGroup> </template>
- 留下你的读书笔记
- 你还没登录,点击这里
-
用户笔记留言