🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] >[success] # 使用 transition 标签实现单元素组件的过渡和动画效果 接下来讲解一下 **vue** 里面关于 **动画** 和 **过渡效果** 的一些 **封装** ,使用这些**封装** , 能让我们更加的方便去编写 **vue** 里面的 **动画** 和 **过渡效果** 。 >[success] ## 单组件的入场 / 出场动画(过渡效果) **单元素** 或 **单组件** 的 **入场 / 出场** 动画,只控制一个 **元素(或组件)** ,的**显示隐藏** 状态,就叫做 **单元素** 或 **单组件** ,元素从 **展示变成隐藏这就叫做出场** ,从 **隐藏变成展示就叫做入场** , **vue** 给我们提供了 **transition** 标签,我们可以通过 **transition** 标签来做 **入场、出场动画** 。 **transition** 需要配合着一些 **样式** 才会好用,具体需要哪些样式呢,如下: | class类名 | 说明 | | --- | --- | | **入场动画**:| | | .v-enter-from | 入场效果, **最初展示的模样** | | .v-enter-active | 入场动画的 **过渡(过程)** | | .v-enter-to | 入场 **动画结束** 时候 | | **出场动画**: | | | .v-leave-from | 出场效果 | | .v-leave-active | 出场动画的过渡(过程) | | .v-leave-to | 入场 **动画结束** 时候 | 使用案例如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>单组件的入场 / 出场动画</title> <style> /* --------------------入场动画----------------------- */ /* 入场效果*/ .v-enter-from { opacity: 0; } /* 入场动画的过渡(过程) */ /* .v-enter-active { transition: opacity 3s ease-out; // 单独写可以自定义过渡效果的不同 }*/ /* 结束时候 */ .v-enter-to { opacity: 1; } /* --------------------出场动画----------------------- */ /* 出场效果(可以不写,因为出场动画出现的一瞬间本身就是显示的状态)*/ .v-leave-from { opacity: 1; } /* 出场动画的过渡(过程) */ /* .v-leave-active { transition: opacity 3s ease-in; // 单独写可以自定义过渡效果的不同 } */ /* 结束时候 */ .v-leave-to { opacity: 0; } /* --------------------上面的.v-enter-active .v-leave-active 可以放在一起写----------------------- */ .v-enter-active, .v-leave-active{ transition: opacity 3s ease-out; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition> <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## 动画效果 动画只需要依赖 **.v-enter-active** 与 **.v-leave-active** 以及一个写好的 **@keyframes** 关键帧动画,即可完成一个动画的效果。 **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>动画效果</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场动画的过渡(过程) */ .v-enter-active { animation: shake 3s; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .v-leave-active { animation: shake 3s; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition> <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## 给class重命名 上面的 **动画类名** 都是 **.v-enter-to** 或 **v-enter-from**,都是以 **v** 开头 ,其实我们是可以给这个 **class 重命名** 的,只需要给 **transition标签** , 添加一个 **name属性** 即可。 **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>给class重命名</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场动画的过渡(过程) */ .hello-enter-active { animation: shake 3s; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .hello-leave-active { animation: shake 3s; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition name="hello"> <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ 注意: 上面的动画不仅可以使用 **v-if** 来控制, 也可以使用 **v-show** 。 >[success] ## 自定义class 同时这个 **transition标签** 的 **class** 也支持 **自定义class** ,自己来依自己的习惯叫自己喜欢的名字,例如想修改 **.v-enter-active** 这个类名,只需要在 **transition标签** 上添加 **enter-active-class** 属性来自定义类名即可 **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>自定义class</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场动画的过渡(过程) */ .hello { animation: shake 3s; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .bay { animation: shake 3s; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition enter-active-class="hello" leave-active-class="bay" > <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## 自定义class结合animate.css使用 **animate.css** 的具体使用教程,请[点击这里](https://animate.style/) 首先 **通过cdn** 的方式引入 **animate.css** ,在使用**animate.css**中的 **任何class动画类名** 都需要与 **animate__animated** 类名一同使用,而且配合使用 **自定义class** ,可以很方便的 **和第三方的动画库相结合** ,例子如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>自定义class结合animate.css使用</title> <!-- 引入animate.css --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition enter-active-class="animate__animated animate__backInDown" leave-active-class="animate__animated animate__flash" > <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## 过渡与动画同时触发 有些时候我们 **不仅希望只有在显示隐藏元素的瞬间有过渡的效果,我们同时希望有动画的效果** ,我们可能还想让 **字体颜色** 也有改变的效果,或者 **坐标位置** 有所改变,等等一系列更多的效果,下面的代码在有 **过渡** 的同时候也改变了 **字体颜色** 。 **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>过渡与动画同时触发</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场效果*/ .v-enter-from{ color: red; } /* 入场动画的过渡(过程) */ .v-enter-active { animation: shake 3s; transition: color 3s ease-in; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .v-leave-active { color: red; animation: shake 3s; transition: all 3s ease-in; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition> <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## 根据animation或transition来决定动画执行时间 接下来看我们的代码中 **animation** 的过渡效果是 **10s** , **transition** 的动画效果是 **3s**,这样动画就会出现 **3s** 的先执行结束,然后漫长的等 **10s** 的过渡效果 **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>根据animation或transition来决定动画执行时间</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场效果*/ .v-enter-from{ color: red; } /* 入场动画的过渡(过程) */ .v-enter-active { animation: shake 10s; transition: color 3s ease-in; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .v-leave-active { color: red; animation: shake 10s; transition: all 3s ease-in; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition> <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ 假如我们有一个需求,想让 **过渡时间与动画时间相同,或动画时间与过渡时间相同** ,该怎么做呢? 我们只需要在 **transition标签** 上添加一个 **type** 属性, **type** 属性等于 **transition** 时,就是 **以过渡效果的时间为准** , **type** 属性等于 **animation** 时,就是 **以动画效果的时间为准** ,代码如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>根据animation或transition来决定动画执行时间</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场效果*/ .v-enter-from{ color: red; } /* 入场动画的过渡(过程) */ .v-enter-active { animation: shake 3s; transition: color 10s ease-in; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .v-leave-active { color: red; animation: shake 3s; transition: color 10s ease-in; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition type="animation"> <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## duration(持续时间)属性 像上面的 **根据animation或transition来决定动画执行时间例子** 很不易懂,我们通过使用 **duration属性实现同样的效果** **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>duration(持续时间)属性</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场效果*/ .v-enter-from{ color: red; } /* 入场动画的过渡(过程) */ .v-enter-active { animation: shake 3s; transition: color 10s ease-in; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .v-leave-active { color: red; animation: shake 3s; transition: color 10s ease-in; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition :duration="1000"> <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ **:duration="1000"** 的意思就是不论是 **过渡时间** 还是 **动画时间** ,都执行 **1000毫秒(1秒)** ,虽然 **css样式** 中写了 **3s** 与 **10s** ,我只执行 **duration** 属性设置的 **1s** 。 **duration属性** 的值也可以设置为 **对象形式** ,**:duration="{ enter:1000, leave: 3000 }"** 意思是 **入场动画1秒,出场动画3秒** 。 **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>duration(持续时间)属性</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场效果*/ .v-enter-from{ color: red; } /* 入场动画的过渡(过程) */ .v-enter-active { animation: shake 3s; transition: color 10s ease-in; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .v-leave-active { color: red; animation: shake 3s; transition: color 10s ease-in; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show } }, template: ` <div> <transition :duration="{ enter:1000, leave: 3000 }"> <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~ >[success] ## 用 js 做动画 我们上面讲解的,还是 **使用css做动画** ,那我们如何使用 **js** 去做动画呢,首先在 **transition标签** 上添加 **:css="false"** ,意思就是 不使用 **transition** 来做 **css动画** ,然后做 **js动画**的话, **vue** 提供给了我们一些 **钩子(某些时刻会自动调用的函数)** ,如下表格: | 钩子名称 | 钩子说明 | 回调参数说明 | | --- | --- | --- | | **入场动画钩子**: | | | @before-enter | 进入入场动画之前时 | **参数1:el:vue返回的dom元素** | | @enter | 开始执行入场动画时 | **参数1:el:vue返回的dom元素<br>参数2:done: done方法可以停止动画** | | @after-enter | 入场动画结束时 | **参数1:el:vue返回的dom元素** | | **出场动画钩子**: | | | @before-leave | 出场动画之前时 | **参数1:el:vue返回的dom元素** | | @leave | 开始执行出场动画时 | **参数1:el:vue返回的dom元素<br>参数2:done: done方法可以停止动画** | | @after-leave | 出场动画结束时 | **参数1:el:vue返回的dom元素** | 具体例子使用方法如下: **index.html** ~~~ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>duration(持续时间)属性</title> <style> /* 关键帧 */ @keyframes shake { 0% { transform: translateX(-100px); } 50% { transform: translateX(-50px); } 100% { transform: translateX(50px); } } /* --------------------入场动画----------------------- */ /* 入场效果*/ .v-enter-from{ color: red; } /* 入场动画的过渡(过程) */ .v-enter-active { animation: shake 3s; transition: color 10s ease-in; } /* --------------------出场动画----------------------- */ /* 出场动画的过渡(过程) */ .v-leave-active { color: red; animation: shake 3s; transition: color 10s ease-in; } </style> <!-- 通过cdn方式引入vue --> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> </body> <script> // 单元素,单组件的入场出场动画 const app = Vue.createApp({ data(){ return { show: false } }, methods: { handleClick(){ this.show = !this.show }, /** * 进入入场动画之前时 * @param {Object} el - vue传递过来的dom元素 */ // ------------------------入场动画--------------------------- handleBeforeEnter(el){ el.style.color = 'red' }, /** * 开始执行入场动画时 * @param {Object} el - vue传递过来的dom元素 * @param {Funcion} done - done方法可以停止动画 */ handleEnterActive(el, done){ // 执行动画 const animate = setInterval(() => { const color = el.style.color if(color === 'red') { el.style.color = 'green' } else { el.style.color = 'red' } }, 1000); // 3秒后清除动画 setTimeout(() => { clearInterval(animate) // 1. 注意:如果不主动调用done方法,程序不会知道动画是否结束, // 也不会去走【动画结束的钩子函数】 done() }, 3000); }, /** * 入场动画结束时 */ handleEnterEnd(){ // 2. 上面的方法中调用【done】后,才会执行该方法中的逻辑 alert('入场动画结束时') }, // ------------------------出场动画--------------------------- /** * 出场动画之前时 * @param {Object} el - vue传递过来的dom元素 */ handleBeforeLeave(el){ alert('出场动画之前') }, /** * 开始执行出场动画时 * @param {Object} el - vue传递过来的dom元素 * @param {Funcion} done - done方法可以停止动画 */ handleLeaveActive(el, done){ alert('开始执行出场动画') done() }, /** * 出场动画结束时 */ handleLeaverEnd(el){ alert('出场动画结束') } }, template: ` <div> <transition :css="false" @before-enter="handleBeforeEnter" @enter="handleEnterActive" @after-enter="handleEnterEnd" @before-leave="handleBeforeLeave" @leave="handleLeaveActive" @after-leave="handleLeaverEnd" > <div v-if="show">hello world</div> </transition> <button @click="handleClick">切换</button> </div>` }) const vm = app.mount('#root') </script> </html> ~~~