[譯] 在項目中集成第三方動畫庫 —— 第二部分

過關斬將

在爲本文查資料時,我發現第三方動畫庫中最多見的動畫類型就是過渡動畫(並不是 CSS transition)了。有些簡單的動畫應用於元素出入頁面的過程。現代單頁應用中最司空見慣的模式就是讓一個元素進入頁面,頂替另外一個離開頁面的元素。想一想這種狀況:第一個元素淡出而第二個元素淡入。這個動畫能夠用於新舊數據交替、按順序移動面板或切換路由到另外一個頁面。Sarah DrasnerGeorgy Marchuk 都列舉了不少此類過渡動畫的優秀案例。javascript

大多數狀況下,動畫庫不支持在過渡動畫中添加或刪除元素。用額外的 JavaScript 代碼實現的庫也許支持這樣的功能,但畢竟這樣的庫不多見,所以咱們如今就來討論如何實現該功能。css

插入單個元素

對於本例,咱們繼續使用 Animate.css,並會用到 fadeInDown 動畫。html

在 DOM 中插入元素有不少種方式,在此再也不贅述。我僅會展現如何用動畫優雅流暢地插入元素,而非讓元素生硬地突現。對於 Animate.css(或其餘相似的庫)來講,這個功能十分簡單。前端

let insertElement = document.createElement('div');
insertElement.innerText = 'this div is inserted';
insertElement.classList.add('animated', 'fadeInDown');

insertElement.addEventListener('animationend', function () {
  this.classList.remove('animated', 'fadeInDown');
});

document.body.append(insertElement);
複製代碼

你如何建立該元素不重要,關鍵在於確保元素插入以前添加了所需的類。而後,此元素就會以優雅的動畫登場。我還監聽了 animationend 事件,用於移除動畫類。一般來講,實現此效果的方式不一而足,而這種方式是最直接的方式了。移除動畫類是爲了方便實現退場效果。咱們不想讓退場動畫和進場動畫相互衝突。vue

移除單個元素

移除單個元素和插入大致相似。目標元素已經存在了,咱們添加所需的動畫類就好。而後在 animationend 事件觸發時,咱們把該元素從 DOM 中移除。在本例中,咱們會使用 Animate.css 提供的 fadeOutDown 動畫,由於它和 fadeInDown 動畫是相互呼應的。java

let removeElement = document.querySelector('#removeElement');

removeElement.addEventListener('animationend', function () {
  this.remove();
});

removeElement.classList.add('animated', 'fadeOutDown');
複製代碼

如你所見,鎖定目標元素、添加類以及在動畫結束時移除元素,一鼓作氣。react

這個過程會有一個問題,那就是隨着目標元素的插入或移除,其餘元素將會在頁面中重排。咱們須要去考慮使用 CSS 技術和一些佈局方式去規避這個問題。android

你方唱罷我登場

如今咱們要切換兩個元素,一進一出。條條大路通羅馬,但我舉的例子是上文中兩個例子的結合。ios

參見 CodePen 上來自 Travis Almand(@talmand)的代碼示例:第三方動畫庫:雙元素過渡git

我會把 JavaScript 代碼分塊來說解其原理。首先,咱們建立 button、container 變量分別存儲對應的兩個 DOM 節點。而後,咱們建立 box一、box2 來存儲在 container 中要交換的兩個元素。

let button = document.querySelector('button');
let container = document.querySelector('.container');
let box1 = document.createElement('div');
let box2 = document.createElement('div');
複製代碼

我寫了一個通用函數,用來在每次觸發 animationEnd 時移除動畫類。

let removeClasses = function () {
  box1.classList.remove('animated', 'fadeOutRight', 'fadeInLeft');
  box2.classList.remove('animated', 'fadeOutRight', 'fadeInLeft');
}
複製代碼

第二個函數則是切換功能的核心。首先,咱們肯定當前顯示的是哪一個元素。藉此咱們能夠推斷出哪一個元素切入,哪一個切出。切出元素用 switchElements 函數監聽,預先移除動畫類,避免陷入動畫循環。而後,等切出元素的動畫完成,咱們將其從容器中移除。接下來,給切入元素添加動畫類,並將其插入容器,讓它以動畫登場。

let switchElements = function () {
  let currentElement = document.querySelector('.container .box');
  let leaveElement = currentElement.classList.contains('box1') ? box1 : box2;
  let enterElement = leaveElement === box1 ? box2 : box1;
  
  leaveElement.removeEventListener('animationend', switchElements);
  leaveElement.remove();
  enterElement.classList.add('animated', 'fadeInLeft');
  container.append(enterElement);
}
複製代碼

咱們須要給兩個盒子作一些通用設置。接着,將第一個盒子插入到容器中。

box1.classList.add('box', 'box1');
box1.addEventListener('animationend', removeClasses);
box2.classList.add('box', 'box2');
box2.addEventListener('animationend', removeClasses);

container.appendChild(box1);
複製代碼

最後,給觸發切換的按鈕添加點擊事件監聽。這一系列事件的啓動順序取決於你。在本例中,我打算從按鈕點擊事件開始。我肯定了正在顯示的盒子 —— 即將切出的盒子,給它添加對應的類讓它以動畫切出。而後我監聽切出元素的 animationEnd 事件,觸發該事件會調用切實操縱切換的函數 —— 上面列出的 switchElements 函數。

button.addEventListener('click', function () {
  let currentElement = document.querySelector('.container .box');
  
  if (currentElement.classList.contains('box1')) {
    box1.classList.add('animated', 'fadeOutRight');
    box1.addEventListener('animationend', switchElements);
  } else {
    box2.classList.add('animated', 'fadeOutRight');
    box2.addEventListener('animationend', switchElements);
  }
}
複製代碼

這個例子眼下有個問題:其代碼是專門爲這一狀況寫死的。固然了,它也很易於擴展,也能適應不一樣場景。故此,該例子只是用來理解如何實現這一功能的。還好,一些諸如 MotionUI 這樣的動畫庫支持用 JavaScript 操縱元素的過渡動畫。另外,像 VueJS 這類 JavaScript 框架也支持切換元素的過渡動畫。

我在另外一個例子中展現了一個更靈活的系統。它由一個容器構成,該容器存放着用 data 屬性引用的切入和切出動畫。容器中的兩個元素按照命令切換位置。這個例子的原理是,經過 JavaScript 控制 data 屬性能夠輕鬆改變更畫。Demo 中還有兩個容器,一個用的是 Animate.css 實現動畫;另外一個用的則是 Animista。這個例子代碼量較大,我將不在本文中講解,但這個例子的註釋很充足,感興趣的話能夠看看。

參見 CodePen 上來自 Travis Almand(@talmand)的代碼示例:第三方動畫庫:自定義動畫示例

停下來思考一下……

是全部人都想看到這些動畫嗎?可能有些人會以爲這些動畫浮誇,沒什麼必要,而另外一些人會認爲這些動畫會致使一些問題。就在前不久,WebKit 爲了解決 Vestibular Spectrum Disorder 問題,引入了 prefers-reduced-motion 媒體查詢功能。Eric Bailey 也針對該媒體查詢功能發表了一篇詳盡的說明文章,和一篇關於最佳實踐的跟進文章。務必看看這些資料。

那麼,你選擇的動畫庫支持 prefers-reduced-motion 嗎?若是官方文檔沒說能支持,那你最好假設不支持。就算官方文檔語焉不詳,你還能夠查看動畫庫的代碼來肯定是否支持,這也容易。例如,Animate.css 在 _base.scss 文件中就有關於媒體查詢的代碼。

@media (print), (prefers-reduced-motion) {
  .animated {
    animation: unset !important;
    transition: none !important;
  }
}
複製代碼

若是你選擇的動畫庫不支持媒體查詢,那麼看到這裏的代碼,你也會知道如何本身動手寫一個補丁。若是該庫使用一個通用類 —— 好比 Animate.css 的 animated 類 —— 那你以這個通用類爲目標就好了。若是沒有這樣一個通用類,那你能夠選某個特定的動畫類或者本身寫一個來實現。

.scale-up-center {
  animation: scale-up-center 0.4s cubic-bezier(0.390, 0.575, 0.565, 1.000) both;
}

@keyframes scale-up-center {
  0% { transform: scale(0.5); }
  100% { transform: scale(1); }
}

@media (print), (prefers-reduced-motion) {
  .scale-up-center {
    animation: unset !important;
    transition: none !important;
  }
}
複製代碼

能夠看到,我比照 Animate.css 中的代碼改造了 Animista 中的動畫類。記住,你得把所選庫中每一個動畫類都作這樣的處理。在 Eric 的文章中,他建議對全部動畫都作漸進加強,以此減小代碼並提升用戶體驗。

讓框架幫你幹力氣活

在不少方面,React、Vue 這些五花八門的框架,讓第三方 CSS 動畫庫比原生 JavaScript 更加易用,由於你不須要手動接駁那些類或者 animationend 事件。你能夠用框架提供的現成功能實現所需效果。使用框架的便利之處在於框架還提供多種操縱動畫的方式,知足多種項目需求。下面的例子展現的只是冰山一角。

hover 效果

對於 hover 效果,我建議用 CSS(一如上文中的建議)來設置會比較好。若是你確實須要在 Vue 之類的框架中用 JavaScript 實現,將會是這樣:

<button @mouseover="over($event, 'tada')" @mouseleave="leave($event, 'tada')">
  tada
</button>
複製代碼
methods: {
  over: function(e, type) {
    e.target.classList.add('animated', type);
  },
  leave: function (e, type) {
    e.target.classList.remove('animated', type);
  }
}
複製代碼

和上面列舉的原生 JavaScript 方案沒有太多不一樣。 一樣地,有不少辦法實現該效果。

奪目動畫(Attention seekers)

設置吸引用戶注意力的動畫效果很是容易。咱們只需添加類名便可,仍然用 Vue 爲例:

<div :class="{animated: isPulse, pulse: isPulse}">pulse</div>

<div :class="[{animated: isBounce, bounce: isBounce}, 'infinite']">bounce</div>
複製代碼

在 pulse 效果中,當布爾變量 isPulse 的值爲 true 時,就會給元素添加兩個類名。在 bounce 效果中,當布爾變量 isBounce 的值爲 true 時,就會給元素添加 animated 類和 bounce 類。infinite 這個類是默認啓用的,這樣在布爾變量 isBounce 的值變爲 false 以前,bounce效果會一直持續。

過渡動畫

幸虧,Vue 的過渡組件能夠經過自定義過渡類輕鬆支持第三方動畫類。其餘庫 —— 好比 React —— 也有相似的功能或插件。要在 Vue 中使用動畫類,咱們只需在過渡組件中實現便可。

<transition enter-active-class="animated fadeInDown" leave-active-class="animated fadeOutDown" mode="out-in" >
  <div v-if="toggle" key="if">if example</div>
  <div v-else key="else">else example</div>
</transition>
複製代碼

使用 Animate.css 時,咱們僅僅使用必要的類便可。要實現 enter-active 效果,咱們使用 animatedfadeInDown 便可。要實現 leave-active 效果,咱們使用 animatedfadeOutDown 便可。在過渡過程當中,這些類會在適當的時候插入。Vue 會替咱們控制類的插入和移除。

想要了解關於在 JavaScript 框架中使用第三方動畫庫的更復雜的例子,請點擊下方連接:

參見 CodePen 上來自 Travis Almand(@talmand)的代碼示例:KLKdJy

來狂歡吧!

這只是一次小小的嘗試,還有更多第三方 CSS 動畫庫等你去探索。有詳細完備的,有別出心裁的,有特點鮮明的,有口味偏重的,也有直接明瞭的。還有針對複雜的 JavaScript 動畫的庫,例如 GreensockAnime.js。甚至還有專門針對字母動畫的庫。

真心但願這些庫可以激發你的靈感,創造你本身的 CSS 動畫。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索