• 列表过渡
    • 列表的进入/离开过渡
    • 列表的排序过渡
    • 列表的交错过渡

    列表过渡

    目前为止,关于过渡我们已经讲到:

    • 单个节点
    • 同一时间渲染多个节点中的一个那么怎么同时渲染整个列表,比如使用 v-for ?在这种场景中,使用 <transition-group> 组件。在我们深入例子之前,先了解关于这个组件的几个特点:

    • 不同于 <transition>,它会以一个真实元素呈现:默认为一个 <span>。你也可以通过 tag 特性更换为其他元素。

    • 过渡模式不可用,因为我们不再相互切换特有的元素。
    • 内部元素 总是需要 提供唯一的 key 属性值。
    • CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。

    列表的进入/离开过渡

    现在让我们由一个简单的例子深入,进入和离开的过渡使用之前一样的 CSS 类名。

    1. <div id="list-demo" class="demo">
    2. <button v-on:click="add">Add</button>
    3. <button v-on:click="remove">Remove</button>
    4. <transition-group name="list" tag="p">
    5. <span v-for="item in items" v-bind:key="item" class="list-item">
    6. {{ item }}
    7. </span>
    8. </transition-group>
    9. </div>
    1. new Vue({
    2. el: '#list-demo',
    3. data: {
    4. items: [1,2,3,4,5,6,7,8,9],
    5. nextNum: 10
    6. },
    7. methods: {
    8. randomIndex: function () {
    9. return Math.floor(Math.random() * this.items.length)
    10. },
    11. add: function () {
    12. this.items.splice(this.randomIndex(), 0, this.nextNum++)
    13. },
    14. remove: function () {
    15. this.items.splice(this.randomIndex(), 1)
    16. },
    17. }
    18. })
    1. .list-item {
    2. display: inline-block;
    3. margin-right: 10px;
    4. }
    5. .list-enter-active, .list-leave-active {
    6. transition: all 1s;
    7. }
    8. .list-enter, .list-leave-to
    9. /* .list-leave-active for below version 2.1.8 */ {
    10. opacity: 0;
    11. transform: translateY(30px);
    12. }

    1 2 3 4 5 6 7 8 9

    这个例子有个问题,当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡,我们下面会解决这个问题。

    列表的排序过渡

    <transition-group> 组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move 特性,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name 属性来自定义前缀,也可以通过 move-class 属性手动设置。

    v-move 对于设置过渡的切换时机和过渡曲线非常有用,你会看到如下的例子:

    1. <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
    2. <div id="flip-list-demo" class="demo">
    3. <button v-on:click="shuffle">Shuffle</button>
    4. <transition-group name="flip-list" tag="ul">
    5. <li v-for="item in items" v-bind:key="item">
    6. {{ item }}
    7. </li>
    8. </transition-group>
    9. </div>
    1. new Vue({
    2. el: '#flip-list-demo',
    3. data: {
    4. items: [1,2,3,4,5,6,7,8,9]
    5. },
    6. methods: {
    7. shuffle: function () {
    8. this.items = _.shuffle(this.items)
    9. }
    10. }
    11. })
    1. .flip-list-move {
    2. transition: transform 1s;
    3. }

    列表过渡 - 图1

    这个看起来很神奇,内部的实现,Vue 使用了一个叫 FLIP 简单的动画队列使用 transforms 将元素从之前的位置平滑过渡新的位置。

    我们将之前实现的例子和这个技术结合,使我们列表的一切变动都会有动画过渡。

    1. <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
    2. <div id="list-complete-demo" class="demo">
    3. <button v-on:click="shuffle">Shuffle</button>
    4. <button v-on:click="add">Add</button>
    5. <button v-on:click="remove">Remove</button>
    6. <transition-group name="list-complete" tag="p">
    7. <span
    8. v-for="item in items"
    9. v-bind:key="item"
    10. class="list-complete-item"
    11. >
    12. {{ item }}
    13. </span>
    14. </transition-group>
    15. </div>
    1. new Vue({
    2. el: '#list-complete-demo',
    3. data: {
    4. items: [1,2,3,4,5,6,7,8,9],
    5. nextNum: 10
    6. },
    7. methods: {
    8. randomIndex: function () {
    9. return Math.floor(Math.random() * this.items.length)
    10. },
    11. add: function () {
    12. this.items.splice(this.randomIndex(), 0, this.nextNum++)
    13. },
    14. remove: function () {
    15. this.items.splice(this.randomIndex(), 1)
    16. },
    17. shuffle: function () {
    18. this.items = _.shuffle(this.items)
    19. }
    20. }
    21. })
    1. .list-complete-item {
    2. transition: all 1s;
    3. display: inline-block;
    4. margin-right: 10px;
    5. }
    6. .list-complete-enter, .list-complete-leave-to
    7. /* .list-complete-leave-active for below version 2.1.8 */ {
    8. opacity: 0;
    9. transform: translateY(30px);
    10. }
    11. .list-complete-leave-active {
    12. position: absolute;
    13. }

    列表过渡 - 图2

    需要注意的是使用 FLIP 过渡的元素不能设置为 display: inline 。作为替代方案,可以设置为 display: inline-block 或者放置于 flex 中

    FLIP 动画不仅可以实现单列过渡,多维网格也同样可以过渡:

    列表过渡 - 图3

    列表的交错过渡

    通过 data 属性与 JavaScript 通信 ,就可以实现列表的交错过渡:

    1. <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
    2. <div id="staggered-list-demo">
    3. <input v-model="query">
    4. <transition-group
    5. name="staggered-fade"
    6. tag="ul"
    7. v-bind:css="false"
    8. v-on:before-enter="beforeEnter"
    9. v-on:enter="enter"
    10. v-on:leave="leave"
    11. >
    12. <li
    13. v-for="(item, index) in computedList"
    14. v-bind:key="item.msg"
    15. v-bind:data-index="index"
    16. >{{ item.msg }}</li>
    17. </transition-group>
    18. </div>
    1. new Vue({
    2. el: '#staggered-list-demo',
    3. data: {
    4. query: '',
    5. list: [
    6. { msg: 'Bruce Lee' },
    7. { msg: 'Jackie Chan' },
    8. { msg: 'Chuck Norris' },
    9. { msg: 'Jet Li' },
    10. { msg: 'Kung Fury' }
    11. ]
    12. },
    13. computed: {
    14. computedList: function () {
    15. var vm = this
    16. return this.list.filter(function (item) {
    17. return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
    18. })
    19. }
    20. },
    21. methods: {
    22. beforeEnter: function (el) {
    23. el.style.opacity = 0
    24. el.style.height = 0
    25. },
    26. enter: function (el, done) {
    27. var delay = el.dataset.index * 150
    28. setTimeout(function () {
    29. Velocity(
    30. el,
    31. { opacity: 1, height: '1.6em' },
    32. { complete: done }
    33. )
    34. }, delay)
    35. },
    36. leave: function (el, done) {
    37. var delay = el.dataset.index * 150
    38. setTimeout(function () {
    39. Velocity(
    40. el,
    41. { opacity: 0, height: 0 },
    42. { complete: done }
    43. )
    44. }, delay)
    45. }
    46. }
    47. })

    列表过渡 - 图4