<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] > [animating-vue](https://hub.fastgit.org/Code-Pop/animating-vue) > [vu 3 - 過渡的 class 名更改](https://vue3js.cn/docs/zh/guide/migration/transition.html) # 為什么需要動畫 您可能聽說過 Web 動畫可以改善 Vue 應用程序的用戶體驗。但是,在 Vue 中該如何實現呢? 在學習如何在Vue中制作動畫之前,讓我們了解一下為什么要這么做,以及探索如何使用動畫來改善用戶體驗。 ## 直擊焦點 在當今世界,人類的頭腦不斷受到信息的轟炸。我發送電子郵件, 接收短信, 滾動信息源... 我們的注意力被引向的無窮無盡的方向。因此,當用戶使用你的應用時,他們的大腦很可能已經處于一天中處理的信息的漩渦中。作為用戶界面構建者,我們的工作是快速引導和指導用戶的注意力,指導他們如何有效地使用我們的應用程序。 我們可以利用動畫的力量來集中用戶的注意力。一旦擁有它,我們就可以把它引導到我們想要去的地方。 ## 激發行動 當用戶到達您的應用程序時,您希望他們做的第一件事是什么? 它可能是您希望他們閱讀的一行文字,或者是被點擊的一個按鈕……您希望他們采取的第一步。 通過有效利用動畫,您可以消除干擾并激發特定的動作。 在此示例中,我們希望激發的行為是用戶單擊按鈕。 通過使用運動(motion),可以集中用戶的注意力,消除一些關于應該如何與我們的應用互動方面的困惑。 動作(motion)之所以能如此有效地吸引注意力,是因為人類的一種原始本能。無論是為了捕食獵物,還是為了避免成為其他動物的獵物,對運動的視覺敏感性都是人類大腦的核心過程,幫助我們在這個星球上生存和進化。 作為開發人員,我們可以利用對視覺運動的敏感性,將用戶的注意力吸引到我們希望他們關注的元素上,以便他們更有可能采取我們希望他們去操作的行為。 但是,強大的力量伴隨著巨大的責任,因此我們應該用一種自然的方式來模擬運動(motion)。 ## 創建流程 如果我開始告訴你要進行動畫處理的下一個原因是什么,最后我要告訴你們,在銀河系中有1000億顆恒星,這就叫做*不合理的推論(non sequitur)*。結束的地方突然從我開始的地方移開了。 不幸的是,在設計糟糕的 Web 界面中,不合理的推論是非常普遍的。會看起來像什么?你是否曾經點擊過一個按鈕或鏈接,而你剛剛看到的所有東西都在一瞬間消失,然后被一個全新的頁面所替代?這可能會令人迷惑,并導致你的用戶不得不在每次類似的事情發生時重新去定位到你的網站。久而久之,這會導致用戶的認知疲勞。 我們利用動畫的,可以讓用戶在瀏覽應用時創建一個無縫的流程,而不需要去破壞上下文。可以將元素變轉換為另一個元素,以一種自然的方式在頁面和組件之間轉換。 我們可以向用戶顯示他們剛打開的菜單去了哪里,通過動畫到把它設置為折疊的狀態。可以使用動畫在應用中創建一種物理空間和時間感,其中元素在可預測的持續時間內在位置和狀態之間轉換。如果這個方法實現得當,用戶就會對我們的應用中的 “世界” 有一種熟悉感。他們會知道這些事物在哪些位置,他們會更好地記住如何找到他們需要的東西,當他們需要它的時候,他們知道應用是如何運作的,這樣他們就不會迷失方向,感到沮喪,或筋疲力盡。 相反,他們會很高興,這就是我認為我們應該對界面進行動畫處理的最后一個主要原因。 ## 喜悅和驚喜 隨著應用程序的數量無窮無盡,其中許多都是相當常規和沉悶的,而我們的用戶將欣賞到美妙的觸感,這些觸感使您的應用更加有趣并且易于使用。如果你的用戶開心了,你就得到了一個粉絲。 這些深思熟慮的觸感可以是任何東西,從一個定制的加載旋轉器(loading spinner),到一個超級簡潔和有用的表單,它減輕了填寫一大堆字段的負擔,到一個按鈕,它傳達了一個成功或錯誤的狀態。 如果做得好,這些令人愉快的觸感將使你的應用更加令人難忘,可以使你從競爭對手中脫穎而出,甚至可以幫助你更長時間地吸引用戶的注意力。但是有個警告:這方面很容易走極端和動畫太多。動畫就像在菜肴上添加香料。太少了,菜就沒有味道了;太多了,菜就毀了。像任何一頓美餐一樣,一個有效的動畫界面是需要去平衡的。 ## 小結 在下面的課程中,我們將看到幾種利用 Vue 創建動畫的方法,這些動畫幾乎可以被任何 web 程序使用。雖然在 Vue 中有很多種動畫的方法,而且它可以變得相當高級和復雜,但是本課程的目標并不是向您展示許多選項中的每一個。目的是為您提供向 Vue 應用添加簡單,實用的動畫所需的工具和見識,這些動畫可指導您集中注意力,激發動作,創造流暢感以及帶來驚喜。 # 轉換(Transitions) 我們理解了動畫界面的價值,可以開始編碼了。但是首先,需要了解 Vue 的轉換組件是如何工作的,然后我們將構建第一個簡單的轉換動畫。 ## Vue 轉換 有人會說轉換和動畫是絕對不同的東西。但是為了這門課程的緣故,我們來實現一個過渡,作為一個簡單的動畫。 轉換正如它聽起來的那樣。就是某些事情從一個階段過渡到另一個階段。從關閉屏幕到打開屏幕,從這里到那里,從打開到關閉,從可見到不可見,等等。因此,轉場可以為用戶提供關于事物如何變化的可視化反饋。 在 Vue 中,可以使用內置的`transition`組件,作為一個包裝器,它為我們提供可以在轉換的生命周期中掛鉤的類。接下來,看看如何利用這些類來定義進入和離開轉換。 ### 默認樣式 在使用 Vue 的`transition`組件設計轉換時,我們首先要問自己這個問題:**默認樣式應該是什么**?換句話說,當元素 / 組件沒有轉換時,它應該如何顯示? 為了更清楚地說明這一點,讓我們來看一個簡單的例子,我們希望一個框在屏幕上進行切換,并在切換過程中淡化其可見性。 它的默認樣式應該是什么?當它沒有過渡的時候,它應該如何表現?在這種情況下,我們希望它默認是`opacity: 1`(不透明度:1),因為隨著可見性的淡入和淡出,這是我們要轉換的樣式。 你可能知道一個 HTML 元素的默認樣式已經是`opacity: 1`所以在這種情況下,我們不需要為我們正在轉換的元素定義一個默認樣式,因為`opacity: 1`已經被瀏覽器定義了。實際上,不需要定義默認樣式,因為該樣式已經是元素默認的顯示方式。想象一下,如果你將一個元素的刻度從 `10%` 轉換到 `100%` ,一個元素的刻度在默認情況下已經是 `100%` 了,所以我們不需要再次明確地定義它。 注意事項,我們需要設置一個默認樣式,該樣式與瀏覽器默認為元素設置樣式的方式不同,但我們稍后會介紹。 一旦我們清楚了元素(或組件)的默認樣式是什么,我們就可以使用`transition`換組件的內置類來設計轉換到或離開默認樣式。換句話說:我們可以構建進入和離開時的轉換。 ### 進入轉換(過渡) 在設計轉換時,我們應該問自己的下一個問題是:起始的樣式應該是什么? 因為我們想要方塊從不可見到可見,我們需要從`opacity: 0`開始.。我們將這個樣式放在`v-enter`類中,以設置轉換的**開始**樣式。 ~~~css .v-enter { /* starting style */ opacity: 0; } ~~~ 現在,當元素進入 DOM 時,它開始時完全不可見,然后從樣式 (`0`) 過渡到我們的默認樣式 (`1`)。這就引出了我們在定義過渡時的下一個問題:激活時樣式應該是什么? 在這種情況下,我們需要知道:過渡(transition)需要多長時間?在過渡期間,我們應該加快還是放慢過渡進程?我們使用`v-enter-active`類來定義過渡處于激活狀態時的行為,并指定其持續時間和 easing 等內容。如果你對 easing 函數還不熟悉,你可以在這里[了解更多](https://www.w3schools.com/cssref/css3_pr_transition-timing-function.asp)。 ~~~css .v-enter { /* starting style */ opacity: 0; } .v-enter-active { /* active entering style */ transition: opacity 2s ease-in; } ~~~ 在這里,我們指定我們正在過渡的`opacity`屬性,并將轉換的持續時間設置為持續`2s`,并給它一個 [`ease-in`](https://cubic-bezier.com/#.42,0,1,1) 曲線,這意味著它將淡入慢慢變快,因為它接近 1。 ## 離開轉換 方塊是在屏幕上,我們如何過渡,如果關閉?在進入轉換中,我們需要定義開始狀態,即`opacity: 0`;對于離開轉換,我們需要定義結束狀態,這就引出了下一個問題:結束樣式應該是什么? 因為需要從可見 (`1`) 過渡到 不可見 (`0`) ,所以在 `v-leave-to` 類中設置了這種結束樣式。 ~~~css .v-enter { /* starting style */ opacity: 0; } .v-enter-active { /* active entering style */ transition: opacity 2s ease-in; } .v-leave-to { /* ending style */ opacity: 0; } ~~~ 現在,方塊 將從默認狀態 `1` 過渡到結束狀態`0`。這就引出了我們在定義轉變時的最后一個主要問題:激活離開的樣式應該是什么? 與進入轉換類似,我們在`v-leave-active`類中定義這個轉換,設置它的持續時間,easing 等等。 ~~~css .v-enter { /* starting style */ opacity: 0; } .v-enter-active { /* active entering style */ transition: opacity 2s ease-in; } .v-leave-active { /* active leaving style */ transition: opacity 2s ease-out; } .v-leave-to { /* ending style */ opacity: 0; } ~~~ 現在我們了解了 Vue 轉換類的機制,接下來讓我們使用 Vue 的`transition`組件來構建我們的第一個實際轉換。 ## 一個簡單的過渡 每當 DOM 中突然出現一些東西時,我們的用戶可能會有點迷惑,他們可能不會立即知道屏幕上發生了什么變化。解決這個問題的一個簡單方法是隨著時間的推移將元素*淡入*視圖,為它們提供有關正在發生變化的上下文。讓我們從前面的`opacity`例子中獲取一些概念,并在我們的例子中實現一個簡單的淡入過渡。 假設我們有一個模態對話框(modal)。它可以是登錄模式、配置模式等等。當點擊`Open`按鈕時,模態漸入。然后模態本身有一個`Close`按鈕,點擊這個按鈕后,模態消失。 *src/views/Home. vue*: ``` <template> <div> <button @click="toggleModal">Open</button> <div v-if="isOpen" class="modal"> <button @click="toggleModal">Close</button> </div> </div> </template> <script> export default { data() { return { isOpen: false } }, methods: { toggleModal() { this.isOpen = !this.isOpen } } } </script> ``` 有一個 `isOpen` data 屬性,當 `toggleModal` 方法運行時,我們在 `true` 和 `false` 之間切換。因為我們的模態 div 中有`v-if="isOpen`,所以每當按下打開和關閉按鈕時,模態就會出現和消失。 通過在`transition`組件中包裹該模態,我們可以在它打開和關閉時為它創建轉換。 *src/views/Home. vue*: ``` <template> <div> <button @click="toggleModal">Open</button> <transition name="fade"> // <-- named transition <div v-if="isOpen" class="modal"> <button @click="toggleModal">Close</button> </div> </transition> </div> </template> ``` ### 命名轉換 注意,這里使用了 `name` 屬性來轉換的命名為 `fade`。這允許我們用這個名字來設置轉換的類名(`fade-enter` 而不是`v-enter`)。命名的過渡幫助我們保持應用程序的規模,并使我們的過渡更可重復使用。我們可能希望在整個應用程序中的其他元素上使用這個`fade`過渡,這就是為什么我們應該根據轉換的作用而不是它們的目標元素來命名轉換的原因。我們可以給這個過渡模態命名,但是這個名字只是描述了這個特定的用例,我們可能想`fade`非模態的內容。 ## 進入轉換 現在我們可以使用命名的轉換類來創建**進入**轉換,這需要定義開始樣式。 還記得我們那些問題嗎? 開始的樣式應該是什么? *src/views/Home. vue*: ```css .fade-enter { /* starting style */ opacity: 0; } ``` 激活進入的樣式應該是什么? *src/views/Home. vue*: ```css .fade-enter { /* starting style */ opacity: 0; } .fade-enter-active { /* entering style */ transition: opacity .5s ease-out; } ``` 在`.fade-enter-active`內定義了CSS `transition`的行為方式,指定了我們`transition`的屬性(`opacity`)轉換的持續時間 (`.5s`)和 timing 函數(`ease-out`)。 ## 離開轉換 現在我們的**進入**轉換已經建立,我們可以創建**離開**轉換。 介紹的樣式應該是什么? *src/views/Home. vue*: ~~~css .fade-leave-to { /* ending style */ opacity: 0; } ~~~ 激活的離開樣式應該是什么? *src/views/Home. vue*: ~~~css .fade-leave-active { /* leaving style */ transition: opacity .5s ease-out; } ~~~ 正如您所看到的,過渡的**結束**樣式(`.fade-leave-to`) 的`opacity`為`0`,**離開**樣式為(`.fade-leave-active`)包含與我們的進入轉換相同的 CSS 轉換。因為這部分是一樣的,所以可以這樣壓縮樣式: *src/views/Home. vue*: ~~~css .fade-enter { opacity: 0; } .fade-enter-active, .fade-leave-active { transition: opacity .5s ease-out; } .fade-leave-to { opacity: 0; } ~~~ ## 額外的過渡類 如果查看 Vue 的 Enter/Leave 轉換的[文檔](https://vuejs.org/v2/guide/transitions.html),您還會發現 `v-enter-to` 和 `v-leave` 類。在本課中我們沒有討論它們的原因是,我們正在過渡到的樣式(`opacity: 1`)已經是元素的默認樣式。我們正在從不透明狀態過渡到其他狀態(`opacity: 1`)也是如此。這就是為什么我們不需要在 `v-enter-to`或 `v-leave`中明確設置不透明度為 1 。只有當您正在過渡到(`v-enter-to`)或者離開(`v-leave`)的樣式與元素的默認樣式不同時,或者當您遇到瀏覽器兼容性問題時,您才需要使用這些類,可能會對您有用。 ## 小結 在本課中,我們討論了轉換/過渡的本質,探索了 Vue 的`transition`組件及其內置類的機制,然后構建了我們的第一個簡單轉換,使用這些問題來指導我們的決策: * 默認樣式應該是什么? * 開始樣式應該是什么? * 激活進入的樣式應該是什么? * 結束的樣式應該是什么? * 激活離開的樣式應該是什么? 在下一課中,我們將研究使用這些相同的概念來使用 Vue Router 創建頁面轉換。 # 頁面轉換 了解了如何使用 Vue 的`transition`組件來創建我們自己的轉換,接下來將這個概念應用到單頁面應用中的一個常見用例:**頁面轉換(page transitions)**。 > 首要條件:假設您以及具備了 Vue Router 的工作知識。 ## Vue Router + transition 當用戶瀏覽我們的應用程序時,我們可以通過使用頁面轉換使一個視圖替換另一個視圖的方式變得平滑。看看我們可以多快地在頁面之間(例如路由級別的組件)重用上一節課的`fade`過渡。 目前,在我們的示例應用程序中,有兩個路由:重新命名的`modal`和`about`組件。如果我們打開 App.vue 文件,我們可以看到每個路由以及有了一個`router-link`,以及它下面的`router-view`組件。`router-view`實際上只是一個動態組件,會被`modal`和`about`組件所取代,所以可以用 Vue 的`transition`組件來包裝`router-view`,并在這里使用`fade`轉換來淡出我們路由到的頁面組件。 *src/App. vue*: ~~~ <template> <div id="app"> <div id="nav"> <router-link to="/">Modal</router-link> | <router-link to="/about">About</router-link> </div> <transition name="fade"> <router-view /> </transition> </div> </template> ~~~ 因為 `fade`轉換還只存在于 `Modal.vue` 中,所以需要把它移動到 `App.vue` 的樣式中,這樣就可以在整個應用中使用它。 ## 轉換模式 目前,這種轉換發生的方式是,頁面內容從淡入淡出(fades out)的同時,新頁面就會淡入(fades in),然后內容跳入到一個合適的位置,轉換就會顯得雜亂。我們可以通過使用 Vue 的**轉換模式(transition modes)**來解決這個問題,其中包括: * `in-out`:新元素轉入(transitions in),然后當完成時,當前元素轉出(transitions out) * `out-in`:當前元素轉出,然后當完成時,新元素向內轉入 * (默認情況下,當前元素和新元素將同時進出) Let’s use the`out-in`mode to smooth out our`fade`transition. We do that by specifying it within the`transition`component’s`mode`attribute, like so: 讓我們使用`out-in`模式來平滑`fade`轉換。我們通過在`轉換`組件的 `mode` 屬性中指定它來實現,如下所示: *src/App.vue*: ~~~ <transition name="fade" mode="out-in"> <router-view /> </transition> ~~~ 現在我們可以看到,頁面轉換更加流暢了。在一個頁面淡出后,新的頁面將會淡入。 ## 另一個頁面轉換 看到實現頁面轉換有多簡單之后,希望你可以感到放松,但是你可能會對我們僅僅重復使用已經建立的`fade`轉換感到有點失望。現在讓我們來創建一個更加細致的頁面轉換/過渡。 一種常見頁面相互轉換的方式是水平滾動。例如,您可能會在博客或照片庫中看到這種轉變。我們可以通過在將頁面組件動畫消失和轉換到屏幕上時平移`X`位置來實現此效果(除了像以前一樣淡化`opacity`)。 To start, when a component transforms*onto*the screen, we want it to come in a bit from the right. So we’ll set the**starting**state of the**entering**transition`10px`over in the`X`direction, like so: 首先,當一個組件`轉換到`屏幕上時,我們希望它稍微從右邊進來一點。因此,我們將在`X`方向上設置**進入**過渡的**開始**狀態`10px`,如下所示: *src/App.vue*: ```css .slide-fade-enter { transform: translateX(10px); opacity: 0; /* still fading opacity */ } ``` 注意一下,如何將`opacity`設置為 `0`。 When the component performs its**leaving**transition, where do we want it to \*\*\*\*leave**to**? We can do the inverse of our entering transition, and make it transition`-10px`to the left as it fades away. We’ll define that like so: 當組件執行**離開**轉換時,我們希望它離開到哪里?我們可以對**進入**轉換做相反的操作,使它向左轉換`-10px`,隨著淡出消失。我們這樣定義它: *src/App.vue*: ```css .slide-fade-leave-to { transform: translateX(-10px); opacity: 0; } ``` 最后,我們希望這種過渡在發生時如何表現? 它應該持續多長時間,并且應該具有緩動功能(easing function)嗎? 我們將在`.slide-fade-enter-active`和`.slide-fade-leave-active`類中設置那 **激活(active)** 設置,讓我們: *src/App.vue*: ```css .slide-fade-enter { transform: translateX(10px); opacity: 0; } .slide-fade-enter-active, .slide-fade-leave-active { transition: all 0.2s ease; } .slide-fade-leave-to { transform: translateX(-10px); opacity: 0; } ``` 現在,當我們在瀏覽器中進行測試時,我們看到我們的頁面組件正在淡出,并且以很好的水平滾動的方式顯示。這里仍然在`transition`換組件上使用`mode="out-in"`來平滑這個轉換。 ## 另一個用例 值得注意的是,我們并不局限于只在頁面之間路由時才使用這種轉換。可能你希望在使用 `is` 屬性的組件之間進行轉換。在這種情況下可以使用這種轉換。 ## 小結 在本課中,使用 Vue 的`transition`組件在用戶瀏覽應用時創建平滑的頁面轉換是多么簡單,以及轉換模式如何幫助我們修改轉換的行為。在下面,我們將學習如何使用 Vue 的`transition-group`組件來轉換多個元素 / 組件的組。 # Group Transitions 到目前為止,我們已經學習了如何在DOM 界面上內外轉換單個組件 / 元素;并將這個概念應用于 Vue Router,在路由級頁面組件之間進行轉換。但是,如果我們有一組組件 / 元素,我們想對它們應用相同的轉換呢? 例如,每次向列表添加新項時,我們可能希望該項以特定的方式轉換到(transition into)該組。或者我們可能有一組元素,我們希望以與更新過濾器相同的方式重新排序。為了實現這種基于組的轉換行為,可以利用 Vue 的內置組件的能力,它的功能與組件類似,但是是用于一組組件 / 元素的。 讓我們通過一個簡單的例子來理解它是如何工作的。 ## 一個簡單的例子 在列表中顯示數據是非常常見的,所以讓我們從這里開始。畢竟,列表只是一組元素。假設我們有一個聯系人列表,當我們向列表中添加一個新聯系人時,我們希望它以特定的方式轉換到列表中(到組中) *src/views/List. vue*: ``` <template> <div> <input v-model="newContact" placeholder="Name" type="text" /> <button @click="addContact">Add Contact</button> <ul> <li v-for="(contact, index) in contacts" :key="index"> {{ contact }} </li> <ul> </div> </template> <script> export default { data() { return { newContact: "", contacts: [ "Beau Thabeast", "Cindy Rella", "Alice Wunderlind" ] } }, methods: { addContact() { this.contacts.push(this.newContact) this.newContact = "" } } } </script> ``` 如您所見,我們的起始代碼只包含一個模板,該模板顯示來自數據的聯系人列表,這數據是可以添加到的數組。因此,每次向該組添加新的列表項時都希望可以進行轉換,那么簡單地將`ul`換成**過渡組組件**,如下所示: *src/views/List. vue*: ~~~ <transition-group tag="ul"> <li v-for="contact in contacts" :key="contact"> {{ contact }} </li> </transition-group> ~~~ 注意,這里添加一個標記屬性并將其設置為`ul`,這是因為轉換組(transition-group)會渲染一個包裝了 組本身的元素。雖然 `transition-group` 默認渲染的元素是`span`,但是我們可以將其更改為其他元素,比如`div`,或者是對應當前的示例:ul。您可以將標記看作是用來包裝一組子元素的標記,因為這基本上就是轉換組渲染時所發生的事情。 > 注意:`transition`組件不渲染元素;所以這種行為只屬于轉換組特有的。 轉換組還不會做任何事情,因為就像轉換組件(transition component)一樣,我們將給它指定我們想要應用于組中每個成員的轉換名稱。創建一個轉換,并將其命名為 “slide-up”。 *src/views/List.vue*: ~~~ <transition-group name="slide-up" tag="ul"> <li v-for="contact in contacts" :key="contact"> {{ contact }} </li> </transition-group> ~~~ 然后在`App.vue`文件的樣式部分,創建 slide-up 轉換。 *src/App.vue*: ```css .slide-up-enter { transform: translateY(10px); /* start 10px down*/ opacity: 0; } .slide-up-enter-active { transition: all 0.2s ease; } ``` 在這個簡單的示例中,我們只是將元素轉換到列表中,而不是從列表中移出,因此我們可以刪除 leave 相關的轉換類。 現在,當我們向聯系人列表添加新成員時,我們將看到它們上滑到( slide-up)列表中。 如果你想知道為什么我們使用`10px`而不是`-10px`從頁面的下方開始,這是一個很好的問題!記住,網頁的坐標從頁面左上角的`(0,0)`開始。所以沿著頁面向下,實際上是沿著 y 坐標方向 “向上”。 ## 在初始渲染時觸發過渡 如果刷新頁面,會注意到列表看起來像是在滑入適當的位置。因為整個路由級別的組件都是這樣過渡的(請記住:我們添加了滑動淡入作為頁面過渡)。可是我們特別希望在初次渲染頁面(以及列表)時,列表會向上滑動,可以通過使用`appear`屬性來實現,如下所示: *src/views/List.vue*: ``` <transition-group name="slide-up" tag="ul" appear> <li v-for="contact in contacts" :key="contact"> {{ contact }} </li> </transition-group> ``` 出現屬性是我們告訴轉換組的方式,我們不僅希望每次添加組中的新成員時都執行幻燈片向上轉換,還希望首次呈現整個組 (整個 ul) 時執行幻燈片向上轉換。 現在,當我們刷新頁面并初始加載組件時,我們將看到這些列表項滑動到位(slide-up into place)。 > 注意:出現`appear`屬性也可以用在轉換組件上,它專屬于轉換組的屬性。 ## 在組內移動項目 如果我們要添加一個特性來對列表進行排序,比如字母排序方法,那么當列表中的條目移動到新的位置時會發生什么情況呢? 例如,如果我們的代碼有一個排序按鈕: *src/views/List.vue*: ``` <template> ... <button @click="sortContacts">Sort</button> ... </template> ``` 一種新的字母排序方法: *src/views/List.vue*: ```js methods: { ... sortContacts() { this.contacts = this.contacts.sort() } } ``` 當我們點擊排序按鈕,我們會看到我們的列表項目只是有點突然吸附到他們的新位置。如果我們能夠微調列表中的項目在排序時的移動方式,就會更好一點。我們可以使用`v-move`過渡類來調整元素在組中改變位置時的移動方式。 讓我們添加`v-move`到我們的滑動向上的過渡樣式。注意`v-`前綴需要被轉換的名稱所替換的。 `src/App.vue`: ```css .slide-up-enter { transform: translateX(10px); opacity: 0; } .slide-up-enter-active { transition: all 0.2s ease; } /* v-move => slide-up-move */ .slide-up-move { transition: transform 0.5s ease-out; } ``` 如果在瀏覽器中查看,會看到組的成員正在按照這些樣式移動。如果我們想要加快或者減慢持續時間,或者改變緩和函數,可以根據自己的喜好調整`move`樣式。 如果您想知道`v-move`在底層是如何工作的,它利用了FLIP轉換。 ## 小結 到這里結束了對組過渡基礎的探索。 該主題涉及面很廣,因為可以采用多種方式對數據(以及顯示該數據的元素/組件)進行分組,并且向這些組應用轉換的方法也很多。但是有了這些基礎知識,我希望當你的應用程序調用它們時,你能有信心開始編寫你自己的代碼。 在下一課中,將深入研究 JavaScript 動畫。 # JavaScript Hooks + Velocity 迄今為止,我們一直在學習 Vue 的轉換和轉換組組件,這些組件為我們構建輸入和輸出轉換提供了必要的類。然而,當我們的需求變得更加獨特或復雜時,我們需要開始使用 JavaScript 來構建我們的動畫。 在本課中,我們將研究 Vue 的 JavaScript Hooks,并將其與一個名為 Velocity.js 的有用的動畫庫結合起來,以構建一些更強大的動畫。 ## JavaScript Hooks 什么是 JavaScript Hooks?它們類似于 Vue 的生命周期鉤子(`beforeCreate`、 `created`等)。它們可以看作是 Vue 轉換(Vue transition)生命周期的一系列階段。 下面是它們的列表: before-enter enter after-enter before-leave enter-cancelled leave after-leave leave-cancelled 如您所見,在進入和離開轉換期間都會調用一些鉤子。當它們被調用時,我們可以讓它們觸發方法,這些方法會包含更復雜的行為,而這些是使用 CSS 是無法做到的。 `` <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter" @enter-cancelled="enterCancelled" @before-leave="beforeLeave" @leave="leave" @after-leave="afterLeave" @leave-cancelled="leaveCancelled" > <!-- ... --> </transition> ``` 了解了有哪些鉤子可用,接著就構建第一個基于 javascript 的 Vue 轉換。 ## 拉抽屜式轉換 抽屜過渡 web 應用程序的一個共同特征是抽屜式(drawer-type)組件,它可以滑出以顯示用戶配置文件、導航菜單或儀表板等內容。在我們的開始代碼里面,你會看到我們現在有了一個`Drawer.vue`文件,它包含一個用戶配置文件,點擊一下按鈕就可以在屏幕上彈出和關閉。 借助 JavaScript hook 和 Velocity 動畫庫,我們將把它變成一個從瀏覽器左側滑出的 “抽屜(drawer)”。但首先,讓我們檢查一下起始代碼: *Drawer.vue*: ``` <template> <div> <button @click="isOpen = !isOpen"> My Profile </button> <div v-if="isOpen" class="drawer"> <img src="../assets/avatar.png" alt="avatar" /> <div></div> <div></div> <div></div> <div></div> </div> </div> </template> <script> export default { data() { return { isOpen: false } } } </script> ``` 按鈕用來切換`isOpen`布爾值,這決定了我們是否顯示我們的抽屜。在這之下,我們有一些被限定作用域的樣式: ``` <style scoped> img { height: 2.5em; width: 2.5em; border-radius: 50%; } .drawer { display: flex; flex-direction: column; align-items: center; width: 12em; height: 20em; border-radius: 1%; background-color: #e0e0e0; box-shadow: 0.08em 0.03em 0.4em #ababab; padding-top: 0.7em; } .drawer div { height: 3.5em; width: 95%; margin-top: 0.6em; background-color: #f0f0f0; border: 0.02em solid #ababab; border-radius: 1%; } </style> ``` 接著第一步是在轉換組件中包裝抽屜,并添加我們將要使用的鉤子。 ``` <template> <div> <button @click="isOpen = !isOpen"> My Profile </button> <transition @before-enter="beforeEnter" @enter="enter" @leave="leave" :css="false" > <div v-if="isOpen" class="drawer"> <img src="../assets/avatar.png" alt="avatar" /> <div></div> <div></div> <div></div> <div></div> </div> </transition> </div> </template> ``` 正如您將看到的,我們需要利用:`before-enter()`、`enter()`和`leave()`,它們將觸發指定的方法。我們需要這些方法做什么呢? 至于`beforeEnter()`,設置在抽屜進入界面之前設置它的初始樣式。 使用`enter()`方法,設置抽屜進入時的樣式。 `leave()`方法,設置抽屜在離開界面時轉換到的樣式。 > 注意:我們使用`:css="false"`來告訴 Vue 它不需要處理這里的轉換類,因為我們依賴于 JavaScript 鉤子。 首先需要導入 Velocity。查看`package.json`,會看到這個庫已經安裝在項目中了(所以只需運行`npm i velocity-animate --save-prod`)。 *Drawer.vue*: ```js <script> import Velocity from 'velocity-animate' export default { data() { return { isOpen: false } }, methods: { beforeEnter(el) { // we'll place our starting style here }, enter(el, done) { // style to transition to on enter }, leave(el, done) { // style to transition away to on leave } } } </script> ``` 注意我們的每個方法都有`el`參數。用來訪問正在轉換的 DOM 元素,該例子中就是我們的`div.drawer`。 那么,我們希望我們的抽屜如何開始呢?開始不希望它是可見的,所以它需要`opacity:0`,寬度也要`width: 0`,我們可以通過轉換它的寬度向外擴展。將這些樣式添加到`beforeEnter()`中: *Drawer.vue*: ```js beforeEnter(el) { el.style.opacity = 0 el.style.width = '0em' }, ``` 現在在 enter () 中,我們可以告訴這個方法將抽屜打開并給它一個寬度。我們將使用 Velocity () 方法來動畫我們的`opacity`和`width`。 *Drawer.vue*: ```js enter(el, done) { Velocity( el, // element to animate { opacity: 1, width: '12em' }, // new style rules { duration: 1000, easing: 'easeOutCubic', complete: done } // define how transition happens and complete it ) } ``` 讓我們分析一下這段代碼。我們向`Velocity()`方法傳遞了一些參數,首先傳入需要的元素(el)進行動畫處理,然后給它傳入一個會應用于該元素的新樣式規則的對象。最后,我們傳入一些規則來定義轉換的持續時間,緩和函數:`easeOutCubic`,還有`done`,這是一個當 velocity 動畫完成時將要運行的方法。`done`讓 Vue 知道這個鉤子已經完成,它可以繼續它的生命周期中的下一個鉤子。 > 注意:我們已經使用了`easeOutCubic`寬松函數,可以使用的眾多函數之一。 我們可以感謝 Velocity。但是,如果您使用此參考,請知道通過 Velocity 無法使用`Back`,`Elastic`和`Bounce`功能。 ## 離開樣式 現在我們已經定義了抽屜應該如何進入,讓我們來定義它應該如何離開`leave()`。 *Drawer.vue*: ```js leave(el, done) { Velocity( el, { opacity: 0, width: '0em' }, { duration: 500, easing: 'easeInCubic', complete: done } ) } ``` 在這里,我們再次使用`Velocity()`方法,傳遞`el`進行動畫處理,告訴它返回到我們最初使用的樣式(不可見且沒有寬度) ,并且我們告訴它如何轉換,設置持續時間和緩和函數,然后我們傳遞`done`,這樣 Vue 就知道鉤子什么時候完成了。 Note: It may seem odd to you that we’re using easeInCubic on our leave transition and easeOutCubic on our enter transition. But the “In” and “Out” only refer to the curve of the ease itself. The “In” and “Out” don’t correlate to how you should use them with an enter vs leave transitions. The best way to pick an easing function is to simply try them out and see which feels most natural for the transition you’re building. > 注意:在**離開**轉換中使用`easeInCubic`,在**進入**轉換中使用`easeOutCubic`,這對你來說可能有點奇怪。但是 “In” 和 “Out” 只是指 ease 本身的曲線。“In” 和 “Out” 與輸入和輸出轉換時應該如何使用它們并不相關。選擇一個緩和功能的最好方法就是簡單地嘗試它們,看看哪個對于你正在構建的過渡來說是最自然的。 現在,當我們在瀏覽器中檢查時,我們會看到用戶配置文件抽屜在我們的界面中滑動得很好。 ## Velocity 的強大 你可能會認為所有這些都是過度消耗,因為我們沒有使用 Velocity 來完成 CSS 本身不能完成的任務(除了一些特殊的緩解功能)。那是因為開始總是很簡單的。Velocity 還有更多我們尚未使用的特性,比如彈簧物理(spring physics),它允許我們在界面中創建彈性 / 快速 / 彈性(springy/snappy/bouncy)運動。 為了了解彈簧物理的實際作用,讓我們用一個具有 2 項的數組代替`enter ()`轉換中的`easing`函數: *Drawer.vue*: ```js enter(el, done) { Velocity( el, { opacity: 1, width: '12em' }, { duration: 1000, easing: [60, 10], complete: done } // now with spring physics ) } ``` 數組中的這些數字是什么?第一個 (60) 是張力的量,第二個 (10) 是這個彈簧函數的摩擦量。我們可以調整這些來實現動態效果。例如,摩擦力越小,結束振動的速度就越快。和緩和函數一樣,這些數字也是你想要玩弄的東西,直到你找到正確的組合,就會有很自然的效果。 現在,在瀏覽器中查看,會看到抽屜在打開時稍稍彈開。好玩! ## 另一個例子:卡片 To see another example of spring physics in action, in combination with the transition-group component, check out the Cards.vue component in this lesson’s ending code. You’ll see that the same concepts we just learned can be applied to another use case, where we might want cards to spring into view when a user loads the page. They could display anything from user reviews to pricing or product options… you name it. 要查看實際的彈簧物理示例,并結合過渡組組件,可以在下面查看`Cards.vue`組件代碼。可知剛剛學到的相同概念可以應用于另一個用例,在該用例中,希望用戶加載頁面時將卡片顯示出來。這里可以顯示任何東西,從用戶評論到價格或產品選擇……你能想到的。 *Cards.vue*: ```js <template> <transition-group appear @before-enter="beforeEnter" @enter="enter" :css="false" > <div class="card" v-for="card in cards" :key="card.id"> <p>{{ card.title }}</p> </div> </transition-group> </template> <script> import Velocity from 'velocity-animate' export default { data() { return { cards: [ { title: 'Could contain anything', id: 123 }, { title: 'Endless possibilities', id: 456 } ] } }, methods: { beforeEnter(el) { el.style.opacity = 0 el.style.width = '0em' }, enter(el, done) { Velocity( el, { opacity: 1, width: '12em', rotateZ: '3deg' }, { duration: 1000, easing: [70, 8], complete: done } ) } } } </script> <style scoped> .card { height: 4em; width: 12em; border-radius: 1%; background-color: #e0e0e0; box-shadow: 0.08em 0.03em 0.4em #ababab; padding-top: 1em; } </style> ``` ## 小結 我們學習了如何通過使用 javascript 鉤子從第三方庫觸發動畫邏輯,在接口中構建更復雜的動畫。Velocity 比我們在這個簡短的課程中提到的要強大得多,所以我推薦您繼續使用它提供的功能,盡可能地使用它。 接下來。我們將研究狀態轉換(State Transitions)以及一個更強大的動畫庫: GSAP。 # 介紹GSAP 在上一課中,我們學習了如何結合使用 JavaScript Hooks 和第三方動畫庫 (Velocity.js) 來構建自定義動畫。在本課中,我們將看到另一個不同的動畫庫如何做到這一點的示例,稱為 GreenSock 動畫平臺(GSAP)。然后,通過 GSAP 用多種方式使我們的界面動畫化。 ## 什么是 GSAP? 如果你觀察一下 web 動畫社區的領先開發人員正在使用的工具,你會發現 GSAP 是他們的首選工具。該庫是一個健壯的、高性能的庫,使您能夠動畫任何 JavaScript 可以觸及的內容,并且它默認消除了瀏覽器之間的不一致。 在本課中,我們將關注 GSAP 庫的 TweenMax 部分。就上下文而言,TweenMax 是 GSAP 中基本的 TweenLite 工具的一個功能更加全面的版本。雖然它比它的小兄弟要大,但是 TweenMax 更加強大,由于它附帶的插件,比如 CSS Plugin,它可以為我們處理一些任務。由于 TweenMax 具有有用的插件,因此可以更輕松地開始學習GSAP,但是如果您擔心大小,我建議您在項目中使用配對的TweenLite版本。 ## 第一個 GSAP 動畫 開始使用 TweenMax,先看一個簡單的例子,這與上一課類似,當相應的 JavaScript hook 被觸發時,我們通過我們的方法運行動畫。 下面是我們的起始代碼: *src/views/Simple.vue*: ```css .card { display: block; margin: 0 auto 0 auto; height: 6.5em; width: 6.5em; border-radius: 1%; background-color: #16c0b0; box-shadow: 0.08em 0.03em 0.4em #ababab; } ``` 我們有一個包裝 card div 的轉換組件,該組件將在加載該組件時顯示。 我們使用`before-enter`并輸入 javascript 鉤子以相同的名稱觸發方法,并將`false`綁定到`:css`,因此 Vue 無需擔心添加/刪除過渡類、 現在從 GSAP 導入 TweenMax,把它安裝到演示項目中。您可以通過從終端運行`npm i gsap`自己安裝它。 導入了 TweenMax,可以繼續使用我們的方法。首先,定義在 card 進入我們的界面之前,它應該具有的樣式。我們將在`beforeEnter`方法中設置它。 *src/views/Simple.vue*: ```js methods: { beforeEnter(el) { el.style.opacity = 0 el.style.transform = ‘scale(0,0)’ }, ``` 我們告訴它從不可見開始,其`x`和`y`值的縮放為`0`。在`enter`方法中,將使用 TweenMax`to`實現動畫到一個全尺寸可見的 card 。 *src/views/Simple.vue*: ```js enter(el, done) { TweenMax.to(el, 1, { opacity: 1, scale: 1, onComplete: done }) } ``` 與前面編寫的 Velocity 方法的方式相似。這里使用了`TweenMax.to()`方法,并向其傳遞一些參數。`To()`方法用于定義我們希望動畫轉到哪里,而不是從哪里開始。那么我們希望卡片(card)的樣式過渡到什么樣子呢?我們在這個方法的參數中進行了設置。 第一個參數(`el`)告訴 TweenMax 要動畫哪個元素。(這與 Velocity 完全相同)。第二個參數是動畫持續的時間。第三個參數是一個對象,為 TweenMax 提供了結束樣式,或者動畫樣式。正如你看到的,我們正在使 card 可見 (`opacity:1`) 和完全縮放到 100% 的`scale:1`。與 Velocity 類似,TweenMax 也有一個屬性,可以在該屬性中傳入一個方法,以便在動畫完成時運行。因此,當這個轉換完成時,我們傳入`done`以運行(`onComplete`)。作為提醒,`done`讓 Vue 知道轉換生命周期中的這一步已經完成,因此 Vue 可以繼續下一步。 如果我們在瀏覽器中運行這個程序,我們會看到當卡片出現時,它從不可見變為可見,而它的比例從 0% 增加到 100% 。 很好。繼續使用 TweenMax 來構建一個更復雜的動畫。 ## Staggering Elements 當元素進入 web 界面時,它們交錯進入適當的位置是很常見的。您可以想象通過 API 調用顯示在卡中的數據列表,然后每張卡交錯排列。我們可以使用 TweenMax 輕松完成此操作。 在開始編碼之前值得注意的是,當我們構建這樣的基于第三方的 JavaScript 動畫時,并不總是需要依賴使用轉換 / 轉換組組件及其 JavaScript Hooks。有許多方法可以實現這個目標,使用組件的生命周期方法就是其中之一。所以在這個例子中,我們將依賴組件的`mounted`鉤子。讓我們來看一下代碼: *src/views/Stagger.vue*: ```css <template> <div id="container"> <div v-for="card in cards" :key="card.id" class="card"></div> </div> </template> ... <style scoped> #container { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-evenly; } .card { height: 6.5em; width: 6.5em; border-radius: 1%; background-color: #16c0b0; box-shadow: 0.08em 0.03em 0.4em #ababab; padding-top: 1em; margin-top: 0.5em; margin-right: 0.5em; } </style> ``` 正如您看到的,我們正在使用`v-for`創建一個基于數據的卡片列表。如果我們在瀏覽器中查看這個組件,那么卡片就已經在它們的位置上了。但是在這個示例中,我們希望它們在組件掛載時錯開到位,所以讓我們設置它們。TweenMax 已經被導入,因此可以立即開始使用它。 這就是錯開代碼的樣子: *src/views/Stagger.vue*: ```js mounted() { TweenMax.staggerFrom(’.card’, 0.5, { opacity: 0, y: 200 }, 0.1) } ``` 正如您想,`staggerFrom()`方法使我們能夠通過傳入指令來將元素錯開到位,這些指令告訴元素在哪里以及如何開始錯開進程。所以,我們告訴 TweenMax 要動畫的元素是什么(`.card`)。給它一個持續時間(`0.5`) ,然后傳入過渡指令,告訴元素開始時不可見(`opacity:0`) ,在`y`方向向下 200 像素。最后的附加參數(`0.1`)是每個元素之間的延遲,這意味著每個動畫卡之間將有 0.1 秒的暫停。沒有這個暫停,所有 card 都會在同一時間進來。 現在,如果我們在瀏覽器中查看,我們會看到它們正如我們所希望的那樣緩慢地進入。 要讓它更好一點,可以用下面的方法來動畫它的比例: 現在我們有了適當的位置,我們可以添加到該動畫中以使其更加美觀。假設我們還想在卡片錯開時為比例設置動畫。 *src/views/Stagger.vue*: ```js mounted() { TweenMax.staggerFrom(’.card’, 0.5, { opacity: 0, y: 200, scale: 0 }, 0.1) } ``` 現在當元素交錯放置時,會將它們的大小從 0 擴展到 1(`0%`到`100%`)。太棒了! ## 小結 我們已經學會了如何使用另一個行業標準的基于 JavaScript 的動畫庫 GSAP 來構建動畫,它們以兩種不同的方式發生:當 JavaScript Hooks 觸發轉換組件時,以及當我們的組件的生命周期方法觸發時。 接下來,我們繼續使用 GSAP,并根據變化的狀態進行動畫處理。 # State with GSAP web 應用程序中,顯示不斷變化的數據的情況并不少見。 無論是在線游戲中玩家的實時得分,還是公司或財務統計數據,了解如何以一種不錯的方式顯示這種隨時變化的狀態都是有幫助的。 在本節中,我們將研究使用 GSAP 實現這一目標。 在底層,GSAP 會為我們執行“補間(tweens)”。 但這實際上是什么意思? 補間是中間的簡稱,它是生成將在動畫的開始和結束之間顯示的幀的過程。 例如,介于 1 和 10 之間的值是:2、3、4、5、6、7、8 和 9(以及它們的小數分別為:2.123 …)。 在從 0% 縮放到 100% 的情況下,介于兩者之間的值將大于 0 且小于 100。因此,通過使用GSAP,我們可以自然地和高效地在值之間進行補間。 讓我們看一個示例,其中我們有一些隨時變化的狀態,并將該狀態顯示為條形圖。 這對于實時記分牌之類的東西很有用,或者對于任何顯示實時不斷變化的數據的地方都很有用。 *src/views/State.vue*: ``` <template> <div> <div :style="{ width: number + 'px' }" class="bar"> <span>{{ number }}</span> </div> </div> </template> <script> export default { data() { return { number: 0 } }, methods: { randomNumber() { this.number = Math.floor(Math.random() * (800 - 0)) } }, created() { setInterval(this.randomNumber, 1500) } } </script> <style scoped> .bar { padding: 5px; background-color: #2c3e50; border: 1px #16c0b0 solid; min-width: 20px; } .bar span { color: white; } </style> ``` 為了創建一些虛擬數據,在創建這個組件時,我們使用`setInterval`每 1500 毫秒運行一次 `randomNumber` 方法。`randomNumber` 方法用一個新的值(范圍從 0-800)更新我們的`number`數據,我們的模板使用這個總是變化的數字通過樣式綁定設置 div 的寬度`:style="{ width: number + ‘px’ }"`,在 `span` 中顯示`number`值本身。 按照原樣,我們的條形寬度會隨著數字的變化從一個值跳到另一個值,并且它顯示的`number`只會更新為新的`number`。 但是,如果我們的標尺平穩地增長并縮小到新的寬度值會不會很好? 同時,它顯示的`number`值也可以補間。 讓我們開始: 現在我們可以使用它來生成舊數字和新數字之間的值。要做到這一點,我們將使用`watch`。如果您以前沒有使用過這個選項,那么這是一個內置的 Vue 組件選項,它允許我們監聽一個反應值,當這個值發生變化時,我們可以使用新的和 / 或舊的值執行操作。所以需要監聽`number`值。 *src/views/State.vue*: ``` watch: { number (newValue){ //now what? } } ``` 創建了一個監聽器,它的名字與它正在觀察的數據值((`number`)相同,傳遞了`newValue`。在繼續之前,讓我們創建另一個名為 `tweenedNumber` 的數據值。你馬上就會明白為什么了。 *src/views/State.vue*: ``` data() { return { number: 0, tweenedNumber: 0 } }, ``` 現在有了 `number` 和 `tweenedNumber` 數據值,可以在監聽器中使用 `gsap.to()` 方法使用它們。在這種情況下,我們不需要 GSAP 直接動畫一個元素,我們希望它動畫我們的數據。 因此,不需要傳遞 GSAP 元素來動畫,而是傳遞要動畫的數據: *src/views/State.vue*: ``` watch: { number( newValue ) { gsap.to(this.$data, {}) } }, ``` 使用`this.$data`傳入組件的`data`對象數據。現在 GSAP 可以訪問我們的數據,并且可以更新它的屬性。現在我們可以給這個動畫一個對象,包括有關如何為數據設置動畫的說明。 我們將給它一個 1 秒的持續時間,一個 ease ,并指定我們想要動畫的數據值`newValue`。 *src/views/State.vue*: ``` watch: { number (newValue) { gsap.to ( this.$data, { duration: 1, ease: ‘circ.out’ tweenedNumber: newValue }) } }, ``` 請注意,我們是如何告訴 gsap 更新數據上的 `tweenedNumber` 屬性的 (在`this.$data`上)并將其動畫化為 `newValue`。根據 `gsap.to() `方法的特性,它將通過將其補間或補間到新值來動畫該值,而不是直接跳轉到該值。 換句話說,不是從`1`直接跳到`5`,而是通過從`1`開始逐漸變高并逐漸接近直到達到`5`,而是創建一條平滑的路徑,通過從`1`開始,逐漸地越來越高,越來越近,直到`5`。因為在值之間有了這些,所以可以使用它們來平滑模板中發生的事情。 不要將`bar`樣式綁定到數字,而是將其綁定到`tweenedNumber`上,并同時顯示該數字。 寬度現在將基于這些增量補間的值而擴大和縮小。 您還會注意到,由于我們現在顯示的是tweenedNumber,因此我們看到了所有的小數。 雖然這些有助于平滑補間,但它們看起來并不漂亮,所以我們只需在顯示范圍值的位置添加`.toFixed(0)`即可進行清理。現在,我們將只顯示整數,而不顯示小數部分。 *src/views/State.vue* 最終代碼: ```js <template> <div> <div :style="{ width: tweenedNumber + 'px' }" class="bar"> <span>{{ tweenedNumber.toFixed(0) }}</span> </div> </div> </template> <script> import gsap from 'gsap' export default { data() { return { number: 0, tweenedNumber: 0 } }, watch: { number(newValue) { gsap.to(this.$data, { duration: 1, ease: 'circ.out', tweenedNumber: newValue }) } }, methods: { randomNumber() { this.number = Math.floor(Math.random() * (800 - 0)) } }, created() { setInterval(this.randomNumber, 1500) } } </script> <style scoped> .bar { padding: 5px; background-color: #2c3e50; border: 1px #16c0b0 solid; min-width: 20px; } .bar span { color: white; } </style> ``` ## 小結 在本課程中,我們學習了補間的概念,以及如何使用GSAP在應用程序中更改狀態之間創建平滑的過渡。 在下一課中,我們將學習如何使用GSAP的時間軸來創建包含多個動畫的序列。 # GSAP 時間表 使用 GSAP 時,動畫通常會開始增加復雜性。 有時候,簡單的補間是不夠的,我們需要創建多個動畫,這些動畫可以按順序一起工作。 為此,我們可以使用 GSAP 的時間軸功能,該功能允許我們將一組補間鏈接在一起以創建更復雜的動畫,這些動畫可以一起工作。 ## 第一個 Timeline 從我們的開始代碼中的 `Timeline.vue` 組件開始。 *src/views/Timeline. vue*: ``` <template> <div> <img class="runner first" src="../assets/runner.png" alt="runner" /> <img class="runner second" src="../assets/runner.png" alt="runner" /> <img class="runner third" src="../assets/runner.png" alt="runner" /> </div> </template> ... <style scoped> .runner { display: block; height: 5em; width: 5em; margin-top: 1.5em; } </style> ``` 在模板中,我們有三張跑步者的圖片。過一會兒,我們將讓它們每個都 “跑” 到瀏覽器的右側。因為已經將 gsap 導入到這個組件中,所以我們只需要初始化我們的時間線,這將在掛載的鉤子中完成。 在這里,我們還將為每個參賽者添加一個 `tween` 到時間線上。讓我們從第一名開始; 在這里,使用 `to` 方法。這個方法允許我們創建一個 `tween` 并將其應用到目標元素 (我們正在動畫的元素)。在本例中,我們使用類名`.first`對元素進行動畫處理。告訴該元素在`2`秒鐘的時間內“向右”運行 700 像素,并為該運動提供一個簡單的“ expo.out”。 *src/views/Timeline. vue*: ```js mounted() { let tl = gsap.timeline() tl.to('.first', { x: 700, duration: 2, ease: 'expo.out' }) } ``` 如果我們在瀏覽器中查看,我們會看到右邊 700px 的跑步者 “賽跑”。到目前為止還不錯,現在讓我們再創建兩個到 tweens 中。 *src/views/Timeline. vue*: ``` mounted() { let tl = gsap.timeline({ repeat: -1, repeatDelay: 1 }) tl.to('.first', { x: 700, duration: 2, ease: 'expo.out' }) tl.to('.second', { x: 700, duration: 2, ease: 'expo.out' }) tl.to('.third', { x: 700, duration: 2, ease: 'expo.out' }) } ``` 現在,當這個組件被安裝時,每個參賽者向右 “賽跑” 700 像素。照這樣看來,我們的選手一個接一個地 “起跑”。在第一名運動員到達目的地后,第二名運動員出發,然后是第三名。然而,如果我們想讓我們的運動員同時起飛呢?或者,或者如果我們希望第二名選手在第一名選手出發后半秒開始 ## 位置參數 我們可以通過使用位置參數來更改時間軸內動畫的觸發時間。通過此參數,我們可以定義補間在時間軸的序列中何時發生,并且有幾種方法可以設置該位置。 ### 絕對位置 設置補間在時間軸內的位置的一種方法是設置補間的絕對位置。 這意味著我們提供了一個整數值,它表示從動畫開始到現在的秒數。 因此,如果我們希望動畫在時間軸上發射半秒,則可以將該動畫的位置值設置為0.5,如下所示: *src/views/Timeline. vue*: ```js mounted() { let tl = gsap.timeline({ repeat: -1, repeatDelay: 1 }) tl.to(’.first’, { x: 200, duration: 2, ease: ‘expo.out’ }) tl.to(’.second’, { x: 400, duration: 2, ease: ‘expo.out’ }, 0.5) // now with an absolute position tl.to(’.third’, { x: 600, duration: 2, ease: ‘expo.out’ }) } ``` 現在,第二名選手將開始“跑步” 0.5秒。 ### 相對位置 有時,如果相對于其他動畫設置動畫的位置,對我們來說可能更有用。 要設置相對位置,我們使用 `-=.75` 或 `+=.75`之類的字符串來設置動畫補間(`-=`)之前或之后(`+=`)補間一個`.75`秒的開始 。 因此,讓我們在時間軸的第二個補間中添加一個相對位置。 *src/views/Timeline. vue*: ```js mounted() { let tl = gsap.timeline({ repeat: -1, repeatDelay: 1 }) tl.to(’.first’, { x: 200, duration: 2, ease: ‘expo.out’ }) tl.to(’.second’, { x: 400, duration: 2, ease: ‘expo.out’ }, '-=.75') tl.to(’.third’, { x: 600, duration: 2, ease: ‘expo.out’ }) } ``` ![](https://img.kancloud.cn/eb/b9/ebb9a187a366716e6170784490228f7a_397x216.png) 現在第二個動畫將在第一個動畫結束前 0.75 秒開始。 ### 開始 / 結束位置 如果我們希望第二個動畫與第一個動畫同時開始,您可能會想著將第二個動畫的位置參數改為 `-=2`。 *src/views/Timeline. vue*: ```js mounted() { let tl = gsap.timeline({ repeat: -1, repeatDelay: 1 }) tl.to('.first', { x: 200, duration: 2, ease: 'expo.out' }) tl.to('.second', { x: 400, duration: 2, ease: 'expo.out' }, '-=2') tl.to('.third', { x: 600, duration: 2, ease: 'expo.out' }) } ``` 雖然可行,但是如果以后需要更改第一個動畫的持續時間呢?例如,如果第一個動畫的持續時間改變為`4`,那么我們的第二個動畫的位置只會向后移動到第一個動畫開始的一半,因為 `4-2=2`。如果我們想讓第一個和第二個補間同時觸發,不管第一個運動員的持續時間有多長,我們可以通過第三種設置位置的方法來實現: 當我們把第二個選手的位置設置為`"<"`時,它就會在第一個動畫開始時開始。`">"` 告訴它會在第一個動畫結束時開始。 *src/views/Timeline. vue*: ```js mounted() { let tl = gsap.timeline({ repeat: -1, repeatDelay: 1 }) tl.to('.first', { x: 200, duration: 2, ease: 'expo.out' }) tl.to('.second', { x: 400, duration: 2, ease: 'expo.out' }, '<') // starts when first tween starts tl.to('.third', { x: 600, duration: 2, ease: 'expo.out' }, '>' ) // starts when second tween starts, which is when first tween starts } ``` 在上面的代碼中,所有三個跑步者將同時開始。為什么?嗯,我們告訴第三名選手在第二名選手開始時開始,第二名選手在第一名選手開始時開始。 ![](https://img.kancloud.cn/5f/96/5f96896d658b2cff0606120b7bf69a96_236x217.png) 我們也可以添加這些,使它們相對。例如,你認為 `<0.5` 的位置會怎樣? *src/views/Timeline. vue*: ```js mounted() { let tl = gsap.timeline({ repeat: -1, repeatDelay: 1 }) tl.to('.first', { x: 200, duration: 2, ease: 'expo.out' }) tl.to('.second', { x: 400, duration: 2, ease: 'expo.out' }, '<0.5') // What will this do? tl.to('.third', { x: 600, duration: 2, ease: 'expo.out' } ) } ``` 如果你猜測第二個動畫會在第一個動畫開始后半秒啟動,同時啟動第三個動畫,那么你是正確的。 如您所見,在時間軸上放置動畫的方式有很多,包括更多可以在此處查看的方式。 ## 時間軸持續 通過這樣一個簡單的時間軸,我們可以很容易地計算出時間軸的總持續時間。然而,在更為復雜的時間線中,我們可以運行 `tl.duration()` ,這將合計所有補間的各個時長,并為我們返回時間軸的總時長。 在確定補間在時間軸序列中的位置時,這可能會有所幫助。 ## 重復 / 循環時間軸 你可能需要重復甚至循環一個時間線。我們可以通過在創建時間軸時傳入一個對象來輕松實現這一點,如下所示: ``` gsap.timeline({ repeat: 2 }) ``` 現在,我們的時間軸將重復兩次。如果我們想讓它在循環中無限地重復,我們會使用 -1。 ``` gsap.timeline({ repeat: -1 }) ``` 還可以在下一次重復之前添加延遲。 ``` gsap.timeline({ repeat: -1, repeatDelay: 1 }) ``` 現在這個時間軸將等待 1 秒鐘再重新開始。 ## 小結 讓我們來看看在這一課中,我們看到了 gsap 時間軸如何允許我們對多個動畫進行排序,并使用`position`參數在它們之間添加或刪除空間。 在最后的課程中,我們將了解如何通過使用嵌套的時間軸來創建更靈活,可伸縮的動畫。 # 嵌套的時間軸 有時候單一的時間軸 對于更復雜的動畫來說是不夠的。接下來,將學習如何通過在一個主時間軸線中嵌套多個時間軸來創建更具可伸縮性、模塊化和可重用的動畫。 打開 `Master.vue` 組件。它目前只有一個時間軸,所以我們將添加一個新的時間軸,看看我們如何在一個新創建的主時間軸中嵌套兩個時間軸。 *src/views/Master.vue*: ``` <template> <div> <div id="container"> <img class="paws first" src="../assets/paws.png" alt="fox-paws" /> <img class="paws second" src="../assets/paws.png" alt="fox-paws" /> <img class="paws third" src="../assets/paws.png" alt="fox-paws" /> <img class="paws fourth" src="../assets/paws.png" alt="fox-paws" /> <img id="fox" src="../assets/fox.png" alt="fox-logo" /> </div> </div> </template> ... <style scoped> #container { display: flex; flex-direction: row; justify-content: center; margin-top: 5em; } #fox { height: 8em; width: 8em; opacity: 0; filter: blur(2px); } .paws { transform: scale(0); width: 2.5em; height: 2.5em; margin-top: 50px; margin-right: 0.8em; opacity: 0; } button { margin-top: 5em; } </style> ``` 我們來分析一下。在我們的模板中,我們有幾張圖片。其中四只是爪子,另一只是狐貍。在`mounted`下,我們有一個 GSAP 時間軸,每個爪子都有一個動畫,通知它在`0.5`秒的持續時間內逐漸淡入(`opacity:1`)并放大(`scale:1`),并具有彈性。注意爪子的位置是 `<.3`,這意味著它們在動畫結束前的 0.3 秒內反彈。 從概念上講,這里沒有我們在前一個示例中介紹的新東西。但是,如果我們的動畫需要變得越來越復雜怎么辦。 例如,我們可能還需要向狐貍添加動畫。 在這種情況下,如果我們創建專門用于對基于爪子的動畫進行排序的時間線,以及為基于狐貍的動畫進行排序的時間線,則可能會有所幫助。 然后,我們可以將它們都添加或嵌套到主時間軸中。 我們可以使用一些方法來創建這些時間線,現在讓我們把以爪子為基礎的時間線重構為一種方法。 Now we have a method that we can call, which will create our timeline and return it. In a moment, you’ll see how we can make this timeline available to a master timeline, but first let’s make a method that creates a new timeline for a fox-based animation. 現在我們有了一個可以調用的方法,它將創建我們的時間線并返回它。稍后,您將看到我們如何使這個時間線可用于主時間線,但首先讓我們提供一個為基于狐貍的動畫創建新時間軸的方法。 *src/views/Master.vue*: ```js foxTL() { let tl = gsap.timeline() tl.to('#fox', { opacity: 1, filter: 'blur(0)', scale: 1, duration: 0.4, ease: 'slow' }) return tl } ``` 現在我們有一個單獨的時間線來放入我們目前所有的基于 fox 的動畫,我們可以在將來添加這些動畫。 > 旁注:因為我們的時間軸正在淡出不透明性和模糊的狐貍,我們只需要添加一些樣式到狐貍的CSS,如下: *src/views/Master.vue*: ```css #fox { height: 8em; width: 8em; opacity: 0; filter: blur(2px); } ``` 這樣,我們的狐貍將開始隱形和模糊,因此,當它動畫進入視野,它不再模糊成為了焦點。 ## 創建主時間軸 現在我們有了兩種方法,每種方法都創建自己的時間軸,我們可以創建一個主時間軸并將這些時間軸添加到其中。 我們將在導入語句下方初始化主時間軸: *src/views/Master.vue*: ``` … import gsap from 'gsap' let masterTL = gsap.timeline() export default { … ``` 雖然我們可以像以前一樣使用我們的`mounted`掛鉤,但是我想展示如何添加交互性來觸發主時間線,因此讓我們添加一個新的`play`方法,當運行時,它將把我們的其他時間軸添加到主時間軸,然后`play()`它。 *src/views/Master.vue*: ```js … methods: { play () { masterTL.add(this.pawsTL()) masterTL.add(this.foxTL()) masterTL.play() }, … ``` 現在需要一個按鈕來觸發這個方法,現在把它添加到模板中: *src/views/Master.vue*: ``` <template> <div> <div id="container"> <img class="paws first" src="../assets/paws.png" alt="fox-paws" /> <img class="paws second" src="../assets/paws.png" alt="fox-paws" /> <img class="paws third" src="../assets/paws.png" alt="fox-paws" /> <img class="paws fourth" src="../assets/paws.png" alt="fox-paws" /> <img id="fox" src="../assets/fox.png" alt="fox-logo" /> </div> <button @click="play">Play</button> </div> </template> ``` 現在有了一個可以點擊的按鈕了,它會把嵌套的爪子時間軸和狐貍時間軸添加到我們的主時間線上,然后播放整個時間軸: *src/views/Master.vue*: ``` <template> <div> <div id="container"> <img class="paws first" src="../assets/paws.png" alt="fox-paws" /> <img class="paws second" src="../assets/paws.png" alt="fox-paws" /> <img class="paws third" src="../assets/paws.png" alt="fox-paws" /> <img class="paws fourth" src="../assets/paws.png" alt="fox-paws" /> <img id="fox" src="../assets/fox.png" alt="fox-logo" /> </div> <button @click="play">Play</button> </div> </template> <script> import gsap from 'gsap' let masterTL = gsap.timeline() export default { methods: { play() { masterTL.add(this.pawsTL()) masterTL.add(this.foxTL()) masterTL.play() }, pawsTL() { let tl = gsap.timeline() tl.to('.first', { opacity: 1, scale: 1, duration: 0.5, ease: 'bounce.out' }) tl.to( '.second', { opacity: 1, scale: 1, duration: 0.5, ease: 'bounce.out' }, '<.3' ) tl.to( '.third', { opacity: 1, scale: 1, duration: 0.5, ease: 'bounce.out' }, '<.3' ) tl.to( '.fourth', { opacity: 1, scale: 1, duration: 0.5, ease: 'bounce.out' }, '<.3' ) return tl }, foxTL() { let tl = gsap.timeline() tl.to('#fox', { opacity: 1, filter: 'blur(0)', scale: 1, duration: 0.4, ease: 'slow' }) return tl } } } </script> <style scoped> #container { display: flex; flex-direction: row; justify-content: center; margin-top: 5em; } #fox { height: 8em; width: 8em; opacity: 0; filter: blur(2px); } .paws { transform: scale(0); width: 2.5em; height: 2.5em; margin-top: 50px; margin-right: 0.8em; opacity: 0; } button { margin-top: 5em; } </style> ``` ## 為啥不使用 SVGs 呢? 您可能已經注意到,在這些例子中,為了簡化這些概念的教學,我們只是使用 PNG。 在一個廣泛應用于多種設備尺寸的生產級應用程序中,你會希望使用 SVG(可縮放矢量圖形)。 使用 SVG 是一個非常深入和廣泛的主題,我們沒有時間在本課中討論,但是在 Real World Vue 課程中涉及到了它。 # 小結 讓我們在這一課中,我們學習了如何在時間軸內嵌入時間軸,以便在我們的應用中創建更加模塊化和可伸縮的動畫。恭喜你完成動畫制作。希望你現在感覺更自信,更有能力為 Vue 應用引入更多的動作、流動和視覺創意。 # 參考 [VueMastery - Animating Vue](https://coursehunters.online/t/vuemastery-animating-vue/2548) [Vue 中的動畫效果](https://www.cnblogs.com/skyflask/p/10990482.html)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看