Vue 中 CSS 动画原理
<div id="app">
<div v-if="show">hello world</div>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
现在希望 hello world 在隐藏和显示的时候加上渐隐渐现的动画效果。
在想要添加动画的标签外部添加 <transiton></transiton>
标签,Vue 会自动帮助我们构建一个动画的流程。
<transiton></transiton>
包裹的内容有一个过渡的动画效果,可以通过 name 属性给该标签起一个名字。
- 从隐藏状态变成显示状态:
- 从显示状态变成隐藏状态:
fade-enter 中的前缀 fade 是 transition 中 name 定义的名字。如果 transition 没有定义 name 属性,前缀默认为 v。即:v-enter,v-enter-active,……
<head>
<style>
.fade-enter, .fade-leave-to {
opacity: 0;
}
.fade-enter-active, .fade-leave-active {
transition: opacity 3s
}
</style>
</head>
<body>
<div id="app">
<transition name="fade">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
</body>
通过在某一时刻自动向 div 元素上增加一些 class 的底层原理,Vue 帮助我们实现了 CSS 过渡动画效果。
在 Vue 中使用 animate.css 库
使用 @keyframes
实现动画效果
<head>
<style>
@keyframes bounce-in {
0% {
transform: scale(0)
}
50% {
transform: scale(1.5)
}
100% {
transform: scale(1)
}
}
.fade-enter-active {
transform-origin: left center;
animation: bounce-in 1s;
}
.fade-leave-active {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
</style>
</head>
<body>
<div id="app">
<transition name="fade">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
</body>
不使用 transition 标签默认提供的类名
当使用 transition 标签的时候,设置了 name=“fade”,它就会自动添加 fade-enter-active、fade-leave-active 这样子的 class 的名字。
如果我想自定义 class,不用默认提供的命名规范可以吗?
可以,只需要在 transition 外面自定义 class 名字:
<transition name="fade" enter-active-class="active" leave-active-class="leave">
<div v-if="show">hello world</div>
</transition>
.active {
transform-origin: left center;
animation: bounce-in 1s;
}
.leave {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
使用 animate.css 库
<head>
<!-- …… -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
</head>
<body>
<div id="app">
<transition name="fade" enter-active-class="animate__animated animate__swing"
leave-active-class="animate__animated animate__shakeY">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
</body>
-
引入 animate.css 动画库(教程:https://animate.style/#usage)
复杂的动画不再需要自己去写,直接用库里提供的动画效果即可。
-
必须使用自定义 class 名字的形式来使用 animate.css
-
class 类里面必须包含
animate__animated
这样一个具体的类,然后根据需求不同,把动画效果的名字写在第二个类名的位置。(enter-active-class="animate__animated animate__swing"
)动画效果的名字可以在官网进行复制(点击下图中的复制符号即可)
在 Vue 中同时使用过渡和动画
如何在第一次加载显示的时候也具备动画的效果?
上面的例子中,只是在点击按钮的时候触发动画效果。如果想要在第一次加载显示的时候也具备动画的效果,该如何去做?
增加一个自定义的 class 名字 appear-active-class
(也要同时增加 appear
属性):
<transition
name="fade"
appear
enter-active-class="animate__animated animate__swing"
leave-active-class="animate__animated animate__shakeY"
appear-active-class="animate__animated animate__swing"
>
<div v-if="show">hello world</div>
</transition>
同时使用过渡和动画
当 @keyframe 形式的动画 (animate__swing) 执行的是 1s,transition 动画执行的时间是 3s 时,整个动画效果会按哪个时间显示呢?
可以手动设置:(type="transition"
)
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
<style>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 3s
}
</style>
</head>
<body>
<div id="app">
<transition type="transition" name="fade" appear
enter-active-class="animate__animated animate__swing fade-enter-active"
leave-active-class="animate__animated animate__shakeY fade-leave-active"
appear-active-class="animate__animated animate__swing">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
</body>
上面是按照 transition 过渡动画的时长作为总时长,能否自定义动画的时长呢?
自定义动画的时长
<transition :duration="5000" name="fade" appear
enter-active-class="animate__animated animate__swing fade-enter-active"
leave-active-class="animate__animated animate__shakeY fade-leave-active"
appear-active-class="animate__animated animate__swing">
<div v-if="show">hello world</div>
</transition>
:duration="5000"
:5000 指的是毫秒。
点击按钮, 1s 动画就执行完了,样式依然存在,5s 后样式才会被清除。
:duration="{enter: 5000, leave: 10000}"
:出场动画 10s 后被移除,入场动画 5s 后 class 被清除。
Vue 中的 Js 动画与 Velocity.js 的结合
上面的例子都是通过 CSS 实现的动画效果,那有没有办法通过 JS 实现动画效果呢?
有的,Vue 提供了一些 JS 动画的钩子。
JS 动画
- 入场动画
@before-enter
当元素即将显示的一瞬间,Vue 会自动触发其绑定的事件。@enter
当 @before-enter 被触发结束之后,就要真正运行动画。当真正开始运行动画效果时,其所有的动画会写在 @enter 钩子对应的回调函数里面。@after-enter
当 done 被调用后,Vue 会触发 @after-enter 事件。
- 出场动画
@before-leave
@leave
@after-leave
<div id="app">
<transition name="fade" @before-enter="handleBeforeEnter" @enter="handleEnter" @after-enter="handleAfterEnter">
<div v-show="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
},
handleBeforeEnter(el) { // el 指的是动画包裹的div标签 (当元素即将显示的一瞬间,Vue会自动触发handleBeforeEnter事件)
el.style.color = "red";
},
handleEnter(el, done) {
// 接收两个参数:第一个参数 el,div的元素;第二个参数 done,回调函数 (当@before-enter被触发结束之后,就要真正运行动画,当真正开始运行动画效果时,其所有的动画会写在@enter钩子对应的回调函数handleEnter里面)
setTimeout(() => {
el.style.color = "green";
// done(); 为了使效果明显一点,写在下面:4s时让动画结束
}, 2000)
setTimeout(() => {
done(); // 当动画结束时,要手动调用 done 这个回调函数,相当于告诉 Vue 动画已经执行完了
}, 4000)
},
handleAfterEnter(el) { //当 done 被调用后,Vue 会触发 @after-enter 事件
el.style.color = "black";
}
}
})
</script>
JS 常用动画库:Velocity.js
下载 velocity.js 后用 <script></script>
标签引入。
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
},
handleBeforeEnter(el) {
el.style.opacity = 0;
},
handleEnter(el, done) {
Velocity(el, {opacity: 1}, {duration: 1000, complete: done})
},
handleAfterEnter(el) {
console.log("动画结束")
}
}
})
Vue 中多个元素或组件的过渡动画
多个元素的过渡动画
.v-enter, .v-leave-to {
opacity: 0;
}
.v-enter-active, .v-leave-active {
transition: opacity 1s;
}
<div id="app">
<transition>
<div v-if="show" key="hello">hello world</div>
<div v-else key="bye">bye world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
transition
提供了 mode
属性:
<transition mode="in-out"> // 先显示再隐藏
多个组件的过渡动画
<div id="app">
<transition mode="in-out">
<child v-if="show"></child>
<child-one v-else></child-one>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
Vue.component('child', {
template: '<div>child</div>'
})
Vue.component('child-one', {
template: '<div>child-one</div>'
})
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
- 也可以通过动态组件来实现:
<div id="app">
<transition mode="in-out">
<component :is="type"></component>
</transition>
<button @click="handleClick">切换</button>
</div>
<script>
Vue.component('child', {
template: '<div>child</div>'
})
Vue.component('child-one', {
template: '<div>child-one</div>'
})
var vm = new Vue({
el: '#app',
data: {
type: 'child'
},
methods: {
handleClick() {
this.type = this.type === 'child' ? 'child-one' : 'child';
}
}
})
</script>
Vue 中的列表过渡
<div id="app">
<div v-for="item of list" :key="item.id">
{{item.title}}
</div>
<button @click="handleBtnClick">Add</button>
</div>
<script>
var count = 0;
var vm = new Vue({
el: '#app',
data: {
list: []
},
methods: {
handleBtnClick() {
this.list.push({
id: count++,
title: 'hello world'
})
}
}
})
</script>
👆每点击一个 Add 就会增加一条数据。
现在我们想往列表中增加内容时实现列表过渡的效果:
<transition-group></transition-group>
:相当于在每一个 div 外层都加了一个<transition></transition>
。<transition-group> <div v-for="item of list" :key="item.id"> {{item.title}} </div> </transition-group>
-
.v-enter, .v-leave-to { opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 1s }
如果能不用 index 作为 key 值,就不要用!!!
Vue 中的动画封装
如果我们觉得某一个动画效果用的比较多,可以对这个动画效果进行封装。
.v-enter, .v-leave-to {
opacity: 0;
}
.v-enter-active, .v-leave-active {
transition: opacity 1s
}
<div id="app">
<fade :show="show">
<div>hello world</div>
</fade>
<button @click="handleBtnClick">切换</button>
</div>
<script>
Vue.component('fade', {
props: ['show'],
template: `
<transition>
<slot v-if="show"></slot>
</transition>
`
})
var vm = new Vue({
el: '#app',
data: {
show: false
},
methods: {
handleBtnClick() {
this.show = !this.show;
}
}
})
</script>
👆 已经把动画效果封装到了 <fade></fade>
组件中,如果想再用这个动画效果,可以直接复制 fade 组件的内容,传不同的 dom 元素进去:
<fade :show="show">
<h1>hello world</h1>
</fade>
样式也可一起封装到动画组件中。
不用 css 动画,转而使用 js 动画:
Vue.component('fade', {
props: ['show'],
template: `
<transition @before-enter="handleBeforeEnter" @enter="handleEnter">
<slot v-if="show"></slot>
</transition>
`,
methods: {
handleBeforeEnter(el) {
el.style.color = "red";
},
handleEnter(el, done) {
setTimeout(() => {
el.style.color = "green";
done();
}, 2000)
}
}
})
可以完整的把所有的动画的实现封装在一个组件里面。
更多 more: