目录

一、使用css创建动画

1、transition过渡动画

2、keyframes动画

二、使用JavaScript的方式实现动画效果

三、vue过渡动画

1、定义过渡动画

2、设置动画过程中的监听回调

 3、多个组件的过渡动画

4、列表过渡动画

四、优化用户列表:


一、使用css创建动画

css3动画的核心是定义keyframes或transition。keyframes定义了动画行为,比如对于颜色渐变的动画,需要定义起始颜色和终止颜色。transition的使用更简单,当组件的css属性发生变化时,使用transition来定义其过度动画的属性。

1、transition过渡动画

<style>
        .demo {
            width: 200px;
            height: 100px;
            background-color: #ccc;
        }

        .demo-transition {
            width: 400px;
            height: 200px;
            background-color: pink;
            transition: width 2s, height 2s, background-color 2s;
        }
    </style>
    <div class="app">
        <div :class="style" @click="run"></div>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    style: 'demo'
                }
            },
            methods: {
                run() {
                    if (this.style == 'demo') {
                        this.style = 'demo-transition'
                    } else {
                        this.style = 'demo'
                    }
                }
            },
        })
        app.mount('.app')
    </script>

结果:

上面的示例实际上使用了简写属性,我们也可以逐条对属性动画效果进行设置。

/* 用来设置动画的属性 */

transition-property: width, height, background-color;

/* 用来设置动画执行的时长 */

transition-duration: 2s;

/* 用来进行延时设置 */

transition-delay: 0s;

/* 用来设置动画的执行方式,linear表示线性 */

transition-timing-function: linear;

2、keyframes动画

 transition动画适合用来创建简单的过渡效果。css3中支持使用animation属性来配置更加复杂的动画效果。animation属性根据keyframes配置来执行基于关键帧的动画效果。

<style>
        @keyframes animation1 {
            0% {
                width: 50px;
                height: 50px;
                background-color: #ccc;
            }

            25% {
                width: 150px;
                height: 150px;
                background-color: green;
            }

            50% {
                width: 200px;
                height: 200px;
                background-color: blue;
            }

            75% {
                width: 250px;
                height: 250px;
                background-color: red;
            }

            100% {
                width: 300px;
                height: 300px;
                background-color: pink;
            }
        }

        .demo {
            width: 50px;
            height: 50px;
            background-color: #ccc;
        }

        .demo-keyframes {
            width: 300px;
            height: 300px;
            background-color: pink;
            animation: animation1 3s linear;
        }
    </style>
    <div class="app">
        <div :class="style" @click="run"></div>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    style: 'demo'
                }
            },
            methods: {
                run() {
                    if (this.style == 'demo') {
                        this.style = 'demo-keyframes'
                    } else {
                        this.style = 'demo'
                    }
                }
            },
        })
        app.mount('.app')
    </script>

结果:

二、使用JavaScript的方式实现动画效果

动画的本质是将元素的变化以渐进的方式完成,即将大的状态变化拆分成非常多个小的状态变化,通过不断执行这些变化也可以实现动画效果。

<div class="app">
        <div class="box" @click="click" :style="{width:width+'px',height:height+'px',backgroundColor:'red'}"></div>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    width: 100,
                    height: 100,
                    timer: null
                }
            },
            methods: {
                click() {
                    this.timer = setInterval(() => {
                        if (this.width >= 200) return
                        this.width++
                        this.height++
                    }, 50)
                }
            },
        })
        app.mount('.app')
    </script>

结果:

三、vue过渡动画

1、定义过渡动画

Vue过渡动画的核心原理依然是采用css类是实现的,只是vue帮助我们在组件的不同生命周期自定切换不同的css类。

Vue中默认提供一个名为transition的内置组件,可以用其来包装要展示过渡动画的组件。transition组件的name属性用来设置要执行的动画名称,Vue中约定了一系列css类名规则来定义各个过渡过程中的组件状态。

x-enter-from类在组件即将被插入页面时添加到组件上,可以理解为组件的初始状态。

x-enter-to类在组件被插入页面后立即被添加,此时x-enter-from类会被移除,可以理解为组件的过渡的最终状态。

x-enter-active类在组件的整个插入过渡动画中都会被添加,直到组件的过渡动画结束后才会被移除。可以在这个类中定义组件过渡动画的时长、方式、延长。

x-leave-from类在组件即将被移除页面时添加到组件上,可以理解为组件移除的初始状态。

x-leave-to则对应地用来设置移除组件动画的终止状态。

x-leave-active类在组件的整个插入过渡动画中都会被添加,直到组件的过渡动画结束后才会被移除。可以在这个类中定义组件过渡动画的时长、方式、延长。

上面的六种特殊css类虽然被添加的实际不同,但是最终都会被移除,因此当动画执行完成后,组件的样式并不会保留,更常见的做法是在组件本身绑定一个最终状态的样式类。

<style>
        .demo {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-enter-from {
            width: 0px;
            height: 0px;
            background-color: red;
        }

        .ani-enter-active {
            transition: width 2s, height 2s, background-color 2s;
        }

        .ani-enter-to {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-leave-from {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-leave-active {
            transition: width 2s, height 2s, background-color 2s;
        }

        .ani-leave-to {
            width: 0px;
            height: 0px;
            background-color: saddlebrown;
        }
    </style>
    <div class="app">
        <button @click="run">点击</button>
        <transition name="ani">
            <div v-if="flag" class="demo"></div>
        </transition>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    flag: false
                }
            },
            methods: {
                run() {
                    this.flag = !this.flag
                }
            },
        })
        app.mount('.app')
    </script>

注意:最终状态.demo css类必需放到最开头位置。

2、设置动画过程中的监听回调

对于组件的加载或卸载过程,有一系列的生命周期函数会被调用。对于vue中的专场动画来说,也可以注册一系列的函数来对其过程进行监听。

<style>
        .demo {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-enter-from {
            width: 0px;
            height: 0px;
            background-color: red;
        }

        .ani-enter-active {
            transition: width 2s, height 2s, background-color 2s;
        }

        .ani-enter-to {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-leave-from {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-leave-active {
            transition: width 2s, height 2s, background-color 2s;
        }

        .ani-leave-to {
            width: 0px;
            height: 0px;
            background-color: saddlebrown;
        }
    </style>
    <div class="app">
        <button @click="run">点击</button>
        <transition name="ani" @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"
            @enter-cancelled="enterCancelled" @before-leave="beforeLeave" @leave="leave" @after-leave="afterLeave"
            @leave-cancelled="leaveCancelled">
            <div v-if="flag" class="demo"></div>
        </transition>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    flag: false
                }
            },
            methods: {
                run() {
                    this.flag = !this.flag
                },
                //组件插入过渡开始前
                beforeEnter(el) {
                    console.log('beforeEnter');
                },
                //组件插入过渡开始
                enter(el, done) {
                    console.log('enter');
                },
                //组件插入过渡后
                afterEnter(el) {
                    console.log('afterEnter');
                },
                //组件插入过渡取消
                enterCancelled(el) {
                    console.log('enterCancelled');
                },
                //组件移除过渡开始前
                beforeLeave(el) {
                    console.log('beforeLeave');
                },
                //组件移除过渡开始
                leave(el, done) {
                    console.log('leave');
                },
                //组件移除过渡后
                afterLeave(el) {
                    console.log('afterLeave');
                },
                //组件移除过渡取消
                leaveCancelled(el) {
                    console.log('leaveCancelled');
                }
            },
        })
        app.mount('.app')
    </script>

 结果:点击按钮:

再次点击按钮

 3、多个组件的过渡动画

Vue中的transition组件支持同时包装多个互斥的子组件元素,从而实现多组件的过渡效果。元素A消失的同时元素B展示。

<style>
        .demo0 {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .demo1 {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-enter-from {
            width: 0px;
            height: 0px;
            background-color: red;
        }

        .ani-enter-active {
            transition: width 3s, height 3s, background-color 3s;
        }

        .ani-enter-to {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-leave-from {
            width: 100px;
            height: 100px;
            background-color: blue;
        }

        .ani-leave-active {
            transition: width 3s, height 3s, background-color 3s;
        }

        .ani-leave-to {
            width: 0px;
            height: 0px;
            background-color: red;
        }
    </style>
    <div class="app">
        <button @click="run">显示/隐藏</button>
        <transition name="ani" mode="out-in">
            <div class="demo0" v-if="show"></div>
            <div class="demo1" v-else></div>
        </transition>
    </div>
    <script>
        const app = Vue.createApp({
            data() {
                return {
                    show: false
                }
            },
            methods: {
                run() {
                    this.show = !this.show
                }
            },
        })
        app.mount('.app')
    </script>

当我们在transition标签上设置mode等于out-in是先移除动画在插入动画,in-out相反。

4、列表过渡动画

在Vue中,通常使用v-for指令来动态构建列表视图。在动态构建列表视图的过程中,其中的元素经常会有增删、重排等操作,在vue中使用transition-group组件可以非常方便地实现列表元素变动的动画效果。

避坑:table中列表过渡动画失效

<style>
        .list-enter-active,
        .list-leave-active {
            transition: all 1s ease;
        }

        .list-enter-from,
        .list-leave-to {
            opacity: 0;
        }
    </style>
    <div class="app">
        <div class="table">
            <table border="1" align="center" width="200px">
                <thead>
                    <th>姓名</th>
                    <th>年龄</th>
                    <th>删除</th>
                </thead>
                <tbody align="center">
                    <transition-group name="list">
                        <tr v-for="(item,index) in getUser" :key="index">
                            <td>{{item.name}}</td>
                            <td>{{item.age}}</td>
                            <td @click="del(index)" style="cursor:pointer">删除</td>
                        </tr>
                    </transition-group>
                </tbody>
            </table>
        </div>
    </div>
    <script>
        let mock = [
            { name: '张三', age: 20 },
            { name: '李四', age: 30 },
            { name: '王五', age: 40 },
            { name: '罗翔', age: 30 }
        ]
        const app = Vue.createApp({
            data() {
                return {
                    getUser: []
                }
            },
            mounted() {
                this.getUser = mock
            },
            methods: {
                del(index) {
                    this.getUser.splice(index, 1)
                }
            },
        })
        app.mount('.app')
    </script>

失效!

四、优化用户列表:

<style>
    .app {
        width: 200px;
        margin: 100px auto;
    }

    .table {
        border: 1px solid black;
        margin-top: 10px;
    }

    .item {
        width: 100px;
        height: 50px;
        border: 1px solid #ccc;
        line-height: 50px;
    }

    .thead {
        display: flex;
    }

    .tbody {
        display: flex;
    }

    .list-enter-from {
        opacity: 0;
    }

    .list-enter-active {
        transition: all 2s ease;
    }

    .list-enter-to {
        opacity: 1;
    }

    .list-leave-from {
        opacity: 1;
    }

    .list-leave-active {
        transition: all 2s ease;
    }

    .list-leave-to {
        opacity: 0;
    }
</style>

<body>
    <div class="app">
        <div class="radio">
            <input type="radio" :value="-1" v-model="sex">全部
            <input type="radio" :value="0" v-model="sex">男
            <input type="radio" :value="1" v-model="sex">女
        </div>
        <div class="input">
            搜索:<input type="text" v-model="keyWord">
        </div>
        <div class="table">
            <div class="thead" align="center">
                <div class="name item">姓名</div>
                <div class="sex item">性别</div>
            </div>
            <transition-group name="list">
                <div class="tbody" v-for="(item,index) in data" align="center" :key="index">
                    <div class="name item">{{item.name}}</div>
                    <div class="sex item">{{item.sex==0?'男':'女'}}</div>
                </div>
            </transition-group>
        </div>
    </div>
    <script>
        let mock = [
            { name: 'zs', sex: 1 },
            { name: 'ls', sex: 0 },
            { name: 'ww', sex: 1 },
            { name: 'lx', sex: 0 },
        ]
        const app = Vue.createApp({
            setup() {
                let data = Vue.ref([])
                let keyWord = Vue.ref("")
                let sex = Vue.ref(-1)
                Vue.onMounted(() => {
                    data.value = mock
                })
                let sexFilter = () => {
                    keyWord.value = ""
                    if (sex.value == -1) {
                        data.value = mock
                    } else {
                        data.value = mock.filter((item) => {
                            return item.sex == sex.value
                        })
                    }
                }
                let keyWordFilter = () => {
                    sex.value = -1
                    data.value = mock.filter((item) => {
                        return item.name.search(keyWord.value) != -1
                    })
                }
                Vue.watch(sex, sexFilter)
                Vue.watch(keyWord, keyWordFilter)
                return { data, keyWord, sex }
            }
        })
        app.mount('.app')
    </script>

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐