今天看啥  ›  专栏  ›  sxw1996

Vue动画

sxw1996  · 掘金  ·  · 2020-01-19 02:35
阅读 126

Vue动画

概述

动画分为两种:transitionanimationvue只是将其融入组件中去使用了。

使用场景

  • v-ifv-show显示隐藏组件时添加动画
  • 组件切换实现页面切换,添加过渡动画,淡入淡出效果增强用户体验

transition组件

vue内置组件,用以包裹想要实现动画效果的dom节点,且只能包裹单个元素。

如何实现包裹多个元素?——需要给transition所包裹的子元素属性标签上添加v-ifv-else,需要注意的是:当transition所包裹子元素的标签名相同时,需要给其添加上key值,以免影响dom工作。

transition实现原理

给当前需要呈现变化的dom元素添加上可能的6个class样式:

  • 动画初始状态:v-enter
  • 动画过渡过程:v-enter-active
  • 动画过渡完成状态:v-enter-to

过程:vue自动帮我们添加切换了样式,我们只需设置好切换的类别即可。在dom元素运动前一帧给其绑定一个class属性v-enter,随即消失。来到运动过程前一刻给其class属性变为v-enter-activev-enter-to,随着运动的进行,上述两个class属性逐渐消失。

name属性:transition标签头部可添加name属性用以区分不同动画。

<transition name="fade"></transition>
<!-- vue就会将 .v-enter 变为 .fade-enter 用以私有化动画样式 -->
复制代码

appear属性:动画一开始变展示出来。

<transition appear><transition>
复制代码

type属性:存在transitionanimation结合的动画,并且两者的动画时间存在长短的区别,我们可以再transition标签头部加上type熟悉,用于告诉vue我们的动画是以哪种方式的时间为基准的,避免上下窜动的不和谐效果,保持了两个动画的同步。

transition-group列表过渡

transition的区别:

  • transition-group会生成一个真实的dom节点,默认为span,通过tag="ul"可实现默认切换
  • transition-group必须要有key

pic

小栗子

.v-enter, /* 时间点,开始动画之前元素的起始状态 */
.v-leave-to{ /* 时间点,元素完成动画离开的终止状态 */
    opacity: 0;
    transform: translateX(70px)
}
.v-enter-active, /* 入场动画时间段 */
.v-leave-active{ /* 离场动画时间段 */
    transition: all 0.8s ease;
}

.private-enter,
.private-leave-to{
    opacity: 0;
    transform: translateY(70px)
}
.private-enter-active,
.private-leave-active{
    transition: all 0.9s ease;
}
复制代码
<div id="app">
    <transition>
        <h1 v-show="flag1">使用过渡类名实现动画效果</h1>
    </transition>
    <br>
    <transition name="private">
        <h1 v-show="flag2">demo</h1>
    </transition>
    <input type="button" value="按钮1" @click="flag1 = !flag1">
    <input type="button" value="按钮2" @click="flag2 = !flag2">
</div>
复制代码
const app = new Vue({
    el: '#app',
    data: {
        flag1: true,
        flag2: true
    }
})
复制代码

自定义样式标签

在使用第三方库(如:animate.css)的时候,使用此方式比较多,常应用于退场动画中。

animate.css基于animation来写的,其@keyframe中已经设置了初始值和结束值,所以我们只需要设置一下运动过程即可。譬如下面leave-active-class="animate shake"中的属性值animate是必须的,后面的shake动画样式可去官网中寻找。

栗子

.a{
    opacity: 0;
}
.b{
    transition: opacity 10s;
}
.c{
    opacity: 1;
}
复制代码
<div id="app">
    <transition
        enter-class="a"
        enter-active-class="b"
        enter-to-class="c"
        appear
        leave-active-class="animated shake">
        <h1 v-show="flag">我要运动</h1>
    </transition>
    <button @click="flag = !flag">change</button>
</div>
复制代码
const app = new Vue({
    el: '#app',
    data: {
        flag: true
    }
})
复制代码

栗子

animate.css的其他使用:

<div id="app">
    <transition enter-active-class="bounceIn" leave-active-class="bounceOut">
        <h3 v-if="flag">11111111111</h3>
    </transition>
    <input type="button" value="toggle1" @click="flag=!flag">

    <!-- 设置入场和离场的动画时长 -->
    <transition enter-active-class="bounceIn" leave-active-class="bounceOut" :duration="1000">
        <h3 v-if="flag1" class="animated">222222222222</h3>
    </transition>
    <input type="button" value="toggle2" @click="flag1=!flag1">

    <!-- 分别设置入场和离场的动画时长 -->
    <transition enter-active-class="bounceIn" leave-active-class="bounceOut" :duration="{enter: 100,leave: 1000}">
        <h3 v-if="flag2" class="animated">333333333333</h3>
    </transition>
    <input type="button" value="toggle3" @click="flag2=!flag2">
</div>
复制代码

js钩子函数实现动画

八个钩子函数

  • @before-enter
  • @enter
  • @after-enter
  • @enter-cancelled
  • @before-leave
  • @leave
  • @after-leave
  • @leave-cancelled

综合栗子

.fade-enter{ /* 定义进入过渡的开始状态,元素插入之前生效,元素插入的下一帧移除 */
    opacity: 0;
}
.fade-enter-active{ /* 定义进入过渡生效时的动画状态 */
    transition: opacity 1s;
}
.fade-enter-to{ /* 定义进入过渡的结束状态,过渡动画完成之后移除 */
    opacity: 1;
}
.fade-leave{ 
    opacity: 1;
}
.fade-leave-active{
    transition: opacity 1s;
}
.fade-leave-to{
    opacity: 0;
}

/* ------------分割线---------- */
.slide-enter{
    opacity: 0;
    transform: translateY(20px);
}
.slide-enter-active{
    transition: opacity 1s; /* transition和animation结合使用 */
    animation: slide-in 1s ease-out;
}
.slide-enter-to{
    opacity: 1;
    transform: translateY(0px);
}
.slide-leave{
    opacity: 1;
    transform: translateY(0px);
}
.slide-leave-active{
    transition: opacity 1s;
    animation: slide-out 1s ease-out;
    position: absolute;
}
.slide-leave-to{
    opacity: 0;
    transform: translateY(20px);
}
.slide-mode{
    transition: transform 1s;
}
@keyframes slide-in{
    from{
        transform: translateY(20px);
    }
    to{
        transform: translateY(0px);
    }
}
@keyframes slide-out{
    from{
        transform: translateY(0px);
    }
    to{
        transform: translateY(20px);
    }
}

/* ---------分割线------- */
.a{
    opacity: 0;
}
.b{
    transition: opacity 10s;
}
.c{
    opacity: 1;
}
复制代码
<div id="app">
    <!-- 1 -->
    <button @click="show=!show">点击</button>
    <br>
    <select v-model="animatedName">
        <option value="fade">闪现过渡动画</option>
        <option value="slide">上下滑动过渡动画</option>
    </select>
    <transition :name="animatedName">
        <div v-show="show">我是属性绑定选择样式</div>
    </transition>
    
    <!-- 2 -->
    <br>
    <transition name="fade" appear>
        <div v-show="show">我是私有化提示语</div>
    </transition>
    <transition name="slide" appear>
        <div v-show="show">我是一句提示语</div>
    </transition>
    
    <!-- 3 -->
    <transition name="slide" appear mode="out-in">
        <div v-if="show">sxw</div>
        <p v-else>iu</p>
    </transition>
    
    <!-- 4 -->
    <transition 
        enter-class="a"
        enter-active-class="b"
        enter-to-class="c"
        appear>
        <div v-show="show">自定义样式标签</div>
    </transition>
    
    <!-- 5 -->
    <transition
        enter-active-class="animated bounce"
        leave-active-class="animated shake"
        appear>
        <div v-show="show">引用第三方库</div>
    </transition>
</div>
复制代码

增强用户体验,以上栗子的两个注意点

  • 给目标元素添加动画时,不仅仅针对目标元素,让其附近元素都有一个类似动画效果下移
.slide-mode{
    transition: transform 1s;
}
复制代码
  • 譬如我们需要删除目标动画元素,我们想着的是让目标元素完全离开文档流,下面元素再排列到空缺的位置上
.slide-leave-active{
    transition: opacity 1s;
    animation: slide-out 1s ease-out;
    position: absolute; /* 离开时候脱离文档流 */
}
复制代码

栗子a:js钩子函数实现动画

<button @click="load = !load">js实现动画</button>
<br>
<transition
    @before-enter="beforeEnter"
    @enter="enter"
    @after-enter="afterEnter"
    @enter-cancelled="enterCancelled"
    
    @before-leave="beforeLeave"
    @leave="leave"
    @after-leave="afterLeave"
    @leave-cancelled="leaveCancelled"
    :css = "false"> 
    <!-- 上一行代码告诉vue我们不是使用css去实现动画的,减少了vue一开始去找css的步骤 -->
    <div v-if="load" style="width: 100px;height: 100px;background-color: red"></div>
</transition>
复制代码
const vm = new Vue({
    el: '#app',
    data: {
        load: false,
        divWidth: ''
    },
    methods: {
        beforeEnter(el) {
            console.log(el) // <div v-if="load" style="width: 100px;height: 100px;background-color: red"></div>
            console.log("beforeEnter")
            this.divWidth = 10 // 限定每次初始动画时的div宽度
            el.style.width = this.divWidth + 'px'
        },
        enter(el, done) {
            console.log('enter')
            done(); // 这里需要执行一下done函数才能往下运行其他钩子函数
            let round = 1;
            const timer = setInterval(() => {
                el.style.width = (this.divWidth + round * 10) + 'px'
                round ++
                if(round > 20) {
                    clearInterval(timer)
                    done()
                }
            }, 20)
        },
        afterEnter(el) {
            console.log('afterEnter')
            el.style.width = '300px'
        },
        enterCancelled(el) {
            console.log('enterCancelled')
        },
        beforeLeave(el) {
            console.log('beforeLeave')
            this.divWidth = '300'
            el.style.width = this.divWidth + 'px'
        },
        leave(el, done) {
            console.log('leave')
            let round = 1
            const timer = setInterval(() => {
                el.style.width = (this.divWidth - round * 10) + 'px'
                round ++
                if(round > 20) {
                    clearInterval(timer)
                    done()
                }
            }, 20)
        },
        afterLeave(el) {
            console.log('afterLeave')
        },
        leaveCancelled(el) {
            console.log('leaveCancelled')
        }
    }
})
复制代码

栗子b:js钩子函数实现动画

<div id="app">
    <button @click="addClick">添加列表</button>
    <transition-group tag="ul" name="slide">
        <li 
            v-for="(list,index) in lists"
            @click="removeClick(index)"
            :key="list">
            {{ list }}
        </li>
    </transition-group>
</div>
复制代码
const vm = new Vue({
    el: '#app',
    data: {
        list: [1,2,3]
    },
    methods: {
        addClick() {
            const position = Math.floor(Math.random() * this.list.length) // 生成随机数
            this.list.splice(position, 0, this.list.length + 1) // 添加的位置 删除的元素 添加元素个数
        },
        removeClick(index) {
            this.list.splice(index, 1)
        }
    }
})
复制代码

demo - 小球加入购物车动画实现

<div id="app">
    <input type="button" value="加入购物车" @click="flag=!flag">
    <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
        <div class="ball" v-show="flag" style="width: 15px;height: 15px;background-color: red;border-radius: 50%;"></div>
    </transition>
</div>
复制代码
const app = new Vue({
    el: '#app',
    data: {
        flag: false
    },
    methods: {
        // el表示要执行动画的dom元素
        beforeEnter(el) {
            // 动画入场前,设置元素开始动画的起始位置
            el.style.transform = 'translate(0, 0)'
        },
        enter(el, done) {
            // 设置小球完成动画之后的结束状态
            el.offsetWidth // 强制刷新动画
            el.style.transform = 'translate(150px, 450px)'
            el.style.transition = 'all 1s ease'
            // 执行done函数,完成下面钩子函数
            done()
        },
        afterEnter(el) {
            // 动画完成之后调用afterEnter函数
            // 控制小球的显示与隐藏,直接跳过后半场动画,让flag标识符变为false
            this.flag = !this.flag
            el.style.opacity = 0.5
        }
    }
})
复制代码

demo - 列表动画 - 添加删除功能

li{
    border: 1px dashed #999;
    margin: 5px;
    line-height: 35px;
    padding-left: 5px;
    font-size: 12px;
    width: 100%;
}
li:hover{
    background-color: hotpink;
    transition: all 0.8s ease;
}

.v-enter,
.v-leave-to{
    opacity: 0;
    transform: translateY(80px);
}
.v-enter-active,
.v-leave-active{
    transition: all 0.6s ease;
}
/* 列表的排序过渡,不仅能够设置进场和离场动画,还可以改变定位 */
/* 改变定位需要设置v-move属性,他会在元素改变定位的过程中应用 */
/* v-move和v-leave-active结合使用,使得列表的过渡动画更加平缓柔和! */
.v-move{
    transition: all 0.8s ease;
}
.v-leave-active{
    position: absolute;
}
复制代码
<div id="app">
    <div>
        <label for="">
            Id:
            <input type="text" v-model="id">
        </label>
        <label for="">
            Name:
            <input type="text" v-model="name">
        </label>
        <input type="button" value="添加" @click="add">
    </div>
    <ul>
        <!-- 列表过渡,如果需要过渡的元素是通过v-for循环渲染出来的,就不能使用transition包裹,应该使用transitionGroup -->
        <!-- 若要为v-for循环创建的元素设置动画,必须为每个元素绑定key值 -->
        <!-- appear属性实现页面展示时的入场动画 -->
        <transition-group appear tag="ul">
            <li v-for="(item,index) in list" :key="item + index" @click="del(index)">
                {{item.id}} -- {{item.name}}
            </li>
        </transition-group>
    </ul>
</div>
复制代码
const vm = new Vue({
    el: '#app',
    data: {
        id: '',
        name: '',
        list: [
           { id: 1, name: 'a' },
           { id: 2, name: 'b' },
           { id: 3, name: 'c' }
        ]
    },
    methods: {
        add() {
            this.list.push( {id: this.id,name: this.name })
            this.id = this.name = ''
        },
        del(index) {
            this.list.splice(index, 1)
        }
    }
})
复制代码



原文地址:访问原文地址
快照地址: 访问文章快照