首页 > 编程笔记

Vue transition-group列表过渡详解

在 Vue 中,可以使用 transition 组件实现过渡和动画效果,而渲染整个列表则使用 <transition-group> 组件。

<transition-group> 组件有以下几个特点:
  1. 不同于 <transition>,它会以一个真实元素呈现:默认为一个 <span>,也可以通过 tag 属性更换为其他元素。
  2. 过渡模式不可用,因为我们不再相互切换特有的元素。
  3. 内部元素总是需要提供唯一的 key 属性值。
  4. CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。

列表的进入/离开过渡

下面通过一个例子来学习如何设计列表的进入/离开过渡效果。

【实例】列表的进入/离开过渡。
<style>
    .list-item {
        display: inline-block;
        margin-right: 10px;
    }
    .list-enter-active, .list-leave-active {
        transition: all 1s;
    }
    .list-enter, .list-leave-to{
        opacity: 0;
        transform: translateY(30px);
    }
</style>
<div id="app" class="demo">
    <button v-on:click="add">添加</button>
    <button v-on:click="remove">移除</button>
    <transition-group name="list" tag="p">
        <span v-for="item in items" v-bind:key="item" class="list-item">
          {{ item }}
        </span>
    </transition-group>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
            items: [10,20,30,40,50,60,70,80,90],
            nextNum: 10
           }
        },
        methods: {
            randomIndex: function () {
                return Math.floor(Math.random() * this.items.length)
            },
            add: function () {
                this.items.splice(this.randomIndex(), 0, this.nextNum++)
            },
            remove: function () {
                this.items.splice(this.randomIndex(),1)
            }
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,单击“添加”按钮,向数组中添加内容,触发进入效果,效果如下图所示:


图 1 添加效果

单击“移除”按钮删除一个数,触发离开效果,效果如下图所示:


图 2 删除效果

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

列表的排序过渡

<transition-group> 组件还有一个特殊之处,不仅可以进入和离开动画,还可以改变定位。

要使用这个新功能,只需了解新增的 v-move class,它会在元素改变定位的过程中应用。与之前的类名一样,可以通过 name 属性来自定义前缀,也可以通过 move-class 属性手动设置。

v-move 对于设置过渡的切换时机和过渡曲线非常有用。

【实例】列表的排序过渡。
<script src="lodash.js"></script>
<style>
    .flip-list-move {
        transition: transform 1s;
    }
</style>
<div id="app" class="demo">
    <button v-on:click="shuffle">排序过渡</button>
    <transition-group name="flip-list" tag="ul">
        <li v-for="item in items" v-bind:key="item">
            {{ item }}
        </li>
    </transition-group>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             items: [10,20,30,40,50,60,70,80,90],
            nextNum: 10
           }
        },
        methods: {
            shuffle: function () {
                this.items = _.shuffle(this.items)
            }
        }
    //在指定的DOM元素上装载应用程序实例的根组件
    }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,效果如下图所示:


图 3 页面加载效果

单击“排序过渡”按钮,将会重新排列数字顺序,效果如下图所示:


图 4 重新排列效果

在上面的示例中,Vue 使用了一个叫 FLIP 的简单动画队列,使用其中的 transforms 将元素从之前的位置平滑过渡到新的位置。

列表的交错过渡

通过 data 选项与 JavaScript 通信,就可以实现列表的交错过渡。下面通过一个过滤器的示例看一下效果。

【实例】列表的交错过渡。
<script src="velocity.js"></script>
<div id="app" class="demo">
    <input v-model="query">
    <transition-group
            name="staggered-fade"
            tag="ul"
            v-bind:css="false"
            v-on:before-enter="beforeEnter"
            v-on:enter="enter"
            v-on:leave="leave"
    >
        <li
                v-for="(item, index) in computedList"
                v-bind:key="item.msg"
                v-bind:data-index="index"
        >{{ item.msg }}</li>
    </transition-group>
</div>
<!--引入Vue文件-->
<script src="https://unpkg.com/vue@next"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
            query: '',
            list: [
                { msg: 'apple' },
                { msg: 'almond'},
                { msg: 'banana' },
                { msg: 'coconut' },
                { msg: 'date' },
                { msg: 'mango' },
                { msg: 'apricot'},
                { msg: 'banana' },
                { msg: 'bitter'}
            ]
           }
        },
        computed: {
            computedList: function () {
                var vm = this
                return this.list.filter(function (item) {
                    return item.msg.toLowerCase().indexOf(vm.query.toLowerCase())!== -1
                })
            }
        },
        methods: {
            beforeEnter: function (el) {
                el.style.opacity = 0
                el.style.height = 0
            },
            enter: function (el, done) {
                var delay = el.dataset.index * 150
                setTimeout(function () {
                    Velocity(
                        el,
                        { opacity: 1, height: '1.6em' },
                        { complete: done }
                    )
                }, delay)
            },
            leave: function (el, done) {
                var delay = el.dataset.index * 150
                setTimeout(function () {
                    Velocity(
                        el,
                        { opacity: 0, height: 0 },
                        { complete: done }
                    )
                }, delay)
            }
        }
     //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
在 Chrome 浏览器中运行程序,效果如下图所示:


图 5 页面加载效果

在输入框中输入“a”,可以发现过滤掉了不带 a 的选项,如下图所示:


图 6 过滤掉一些数据

推荐阅读