Vue3中實(shí)現(xiàn)過渡動(dòng)畫的方法小結(jié)
前言
首先拋開 vue 本身,假設(shè)需要給某個(gè) Dom 元素實(shí)現(xiàn)一些過渡動(dòng)畫,那么下面這些事是必須的:
- 實(shí)現(xiàn)目標(biāo)元素不同時(shí)刻下的樣式,常見做法就是抽取在不同的 css 選擇器中
- 根據(jù)不同時(shí)刻切換不同的 css 選擇器以達(dá)到樣式的變化
- 設(shè)置樣式過度的方式和時(shí)間,如:transition: all .5s ease
而在 vue 中實(shí)現(xiàn)過渡動(dòng)畫,那就不得不提到其內(nèi)置的 transition 組件,而 transition 組件幫我們實(shí)現(xiàn)了第一件事,就是對(duì)組件不同時(shí)刻設(shè)置不同的 css 選擇器,甚至于可以對(duì)這個(gè)選擇器進(jìn)行自定義,因此,我們只需要完成后面兩件事即可,當(dāng)然這里對(duì) transition 組件的用法就不過多進(jìn)行介紹.
當(dāng)然這里的實(shí)現(xiàn)動(dòng)畫的方式,會(huì)從下面幾個(gè)方面來實(shí)現(xiàn):
CSS3 transition
屬性CSS3 @keyframes
幀動(dòng)畫- 第三方庫
animate.css
- 第三方庫
gsap —— GreenSock Animation Platform
,在官方文檔中有提及的
<transition> 組件 + CSS3 transition 屬性
話不多說,直接來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 顯示/隱藏 的過渡動(dòng)畫:
- 顯示時(shí):從左側(cè)滑入,且透明度增大
- 隱藏時(shí):從右側(cè)滑出,且透明度減小
// toggle.vue <script setup lang="ts"> import { ref } from 'vue' const isShow = ref(true) const toggle = () => { isShow.value = !isShow.value } </script> <template> <transition name="fade"> <h1 v-if="isShow">this is h1 content!</h1> </transition> <div> <button @click="toggle">toggle</button> </div> </template> <style> .fade-enter-from, .fade-leave-to { opacity: 0; } .fade-enter-from { transform: translateX(-30px); } .fade-enter-to , .fade-leave-to { transform: translateX(30px); } .fade-enter-to, .fade-leave-from { opacity: 1; } .fade-enter-active, .fade-leave-active { transition: all .5s ease; } h1 { width: 300px; margin: 10px auto; } </style>
<transition> 組件 + CSS3 @keyframes 幀動(dòng)畫
css
中除了使用 transition
屬性實(shí)現(xiàn)不同樣式間的過渡,也可以直接使用 @keyframes
來實(shí)現(xiàn)幀動(dòng)畫.
// hang.vue <script setup lang="ts"> import { ref } from 'vue' const isShow = ref(true) const hang = () => { isShow.value = !isShow.value } </script> <template> <transition name="fade"> <h1 v-if="isShow">this is h1 content!</h1> </transition> <div> <button @click="hang">trigger</button> </div> </template> <style> .fade-enter-active { animation: hang 0.5s 1 ease; } .fade-leave-active { animation: hang 0.5s 1 ease reverse; } @keyframes hang { 0% { transform: translateY(30px) rotateZ(30deg); opacity: 0; } 25% { transform: translateY(24px) rotateZ(24deg); opacity: 0.3; } 50% { transform: translateY(18px) rotateZ(18deg); opacity: 0.6; } 75% { transform: translateY(12px) rotateZ(12deg); opacity: 0.8; } 100% { transform: translateY(-5px) rotateZ(0deg); opacity: 1; } } h1 { width: 300px; margin: 10px auto; } </style>
<transition> 組件 + 第三方庫 animate.css
直接使用 animate.css
中對(duì)應(yīng)效果的動(dòng)畫名,配合 animation
屬性進(jìn)行使用即可.
對(duì)應(yīng)的效果和動(dòng)畫名可通過 animat.style 效果預(yù)覽 查看
// animate.vue <script setup lang="ts"> import { ref } from 'vue' import './animate.css' const isShow = ref(true) const hang = () => { isShow.value = !isShow.value } </script> <template> <transition name="fade"> <h1 v-if="isShow">this is h1 content!</h1> </transition> <div> <button @click="hang">trigger</button> </div> </template> <style> .fade-enter-active { animation: flip 0.5s 1 ease; } .fade-leave-active { animation: flip 0.5s 1 ease reverse; } h1 { width: 300px; margin: 10px auto; } </style>
<transition> 組件 + 第三方庫 gsap
通過上面幾個(gè)簡(jiǎn)單的動(dòng)畫例子,其實(shí)不難發(fā)現(xiàn),無論是自己實(shí)現(xiàn)動(dòng)畫,還是使用已有的 css 樣式庫都有一個(gè)缺點(diǎn),就是不夠靈活,比如:某些 css 屬性值只能是一個(gè)不變的值
如果有些場(chǎng)景需要一些更復(fù)雜和靈活的 css 樣式,那么顯然前面提到的方式并不符合實(shí)際需求,換句話說,如果需要在 js 中實(shí)現(xiàn)樣式的過渡,以及設(shè)置動(dòng)態(tài)的 css 樣式屬性值時(shí),就有必要使用一些第三方庫,如:gsap,幫助我們更方便的實(shí)現(xiàn)需求.
// gsap.vue <script setup> import { ref } from "vue"; import gsap from "gsap"; let timeScaleTween = null; const enter = (el) => { const tl = gsap.timeline(), atom = el, dur = 2, del = 0.5; tl.fromTo( ".electron", { rotationX: 90 }, { rotationZ: -360, rotationX: 90, ease: "none", duration: dur, stagger: { each: -del, repeat: -1, }, }, 0 ); tl.to( ".path", { rotationZ: 360, ease: "none", duration: dur, stagger: { each: -del, repeat: -1, }, }, 0 ); // Add a rotation to the whole atom gsap.set(atom, { transformOrigin: "center center" }); gsap.to(atom, { rotation: 360, ease: "none", repeat: -1, duration: 300 }); // Skip the loading tl.progress(0.9999); timeScaleTween = gsap.to(tl, { duration: 0.75, timeScale: 0.1, paused: true, }); }; const slow = () => { timeScaleTween.play() } const reverse = () => { timeScaleTween.reverse() } const isShow = ref(false); const show = () => { isShow.value = true }; </script> <template> <transition name="fade" @enter="enter"> <div class="atom" v-if="isShow"> <div class="orbit"> <div class="path"> <div class="electron"></div> </div> </div> <div class="orbit"> <div class="path"> <div class="electron"></div> </div> </div> <div class="orbit"> <div class="path"> <div class="electron"></div> </div> </div> <div class="orbit"> <div class="path"> <div class="electron"></div> </div> </div> <div class="nucleus"></div> </div> </transition> <div> <button @click="show">show</button> <button @click="slow">slow</button> <button @click="reverse">reverse</button> </div> </template> <style> @import url("https://fonts.googleapis.com/css?family=Signika+Negative:300,400&display=swap"); body { font-family: "Signika Negative", sans-serif; font-weight: 300; background: grey; overflow: hidden; color: white; text-align: center; } .atom { position: absolute; top: 50%; left: 50%; width: 300px; height: 300px; perspective: 1000; margin-left: -170px; margin-top: -146px; transform-style: preserve-3d; } .nucleus { position: absolute; top: 50%; left: 50%; margin: -10px 0 0 -10px; width: 25px; height: 25px; border-radius: 50%; background: #272727; } .orbit { position: absolute; top: 0; left: 0; width: 300px; height: 300px; border-radius: 300px; border: 5px solid #ccc; transform-style: preserve-3d; transform: rotateX(80deg) rotateY(20deg); } .orbit:nth-child(2) { transform: rotateX(80deg) rotateY(70deg); } .orbit:nth-child(3) { transform: rotateX(80deg) rotateY(-20deg); } .orbit:nth-child(4) { transform: rotateX(80deg) rotateY(-50deg); } .path { width: 300px; height: 300px; position: relative; transform-style: preserve-3d; } .electron { position: absolute; top: -5px; left: 50%; margin-left: -5px; width: 10px; height: 10px; border-radius: 10px; background: #ccc; } button{ margin: 10px; } </style>
本文通過四種方式實(shí)現(xiàn)了一些簡(jiǎn)單動(dòng)畫,但其實(shí)在 vue 中實(shí)現(xiàn)動(dòng)畫也不一定要使用 transition 組件,具體場(chǎng)景還是得具體分析.
以上就是Vue3中實(shí)現(xiàn)過渡動(dòng)畫的方法小結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Vue3過渡動(dòng)畫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue3?TS?vite?element?ali-oss使用教程示例
這篇文章主要為大家介紹了vue3?TS?vite?element?ali-oss使用教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Vant?Weapp組件picker選擇器初始默認(rèn)選中問題
這篇文章主要介紹了Vant?Weapp組件picker選擇器初始默認(rèn)選中問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01Vue2集成Lodop插件實(shí)現(xiàn)在線打印功能
這篇文章主要為大家詳細(xì)介紹了Vue2如何集成Lodop插件實(shí)現(xiàn)在線打印功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03Vuex的基本概念、項(xiàng)目搭建以及入坑點(diǎn)
Vuex是一個(gè)專門為Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式,這篇文章主要介紹了Vuex的基本概念、項(xiàng)目搭建以及入坑點(diǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11vue3 onMounted異步函數(shù)同步請(qǐng)求async/await實(shí)現(xiàn)
這篇文章主要為大家介紹了vue3 onMounted初始化數(shù)據(jù)異步函數(shù)/同步請(qǐng)求async/await實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Vue Element UI 表單自定義校驗(yàn)規(guī)則及使用
這篇文章主要介紹了Vue Element UI 表單自定義效驗(yàn)規(guī)則及使用,文中通過代碼介紹了常見表單效驗(yàn)規(guī)則,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02