- 原文地址:Integrating Third-Party Animation Libraries to a Project - Part 2
- 原文做者:Travis Almand
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:Baddyo
- 校對者:lgh757079506
在爲本文查資料時,我發現第三方動畫庫中最多見的動畫類型就是過渡動畫(並不是 CSS transition)了。有些簡單的動畫應用於元素出入頁面的過程。現代單頁應用中最司空見慣的模式就是讓一個元素進入頁面,頂替另外一個離開頁面的元素。想一想這種狀況:第一個元素淡出而第二個元素淡入。這個動畫能夠用於新舊數據交替、按順序移動面板或切換路由到另外一個頁面。Sarah Drasner 和 Georgy 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 效果,我建議用 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 方案沒有太多不一樣。 一樣地,有不少辦法實現該效果。
設置吸引用戶注意力的動畫效果很是容易。咱們只需添加類名便可,仍然用 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
效果,咱們使用 animated
和 fadeInDown
便可。要實現 leave-active
效果,咱們使用 animated
和 fadeOutDown
便可。在過渡過程當中,這些類會在適當的時候插入。Vue 會替咱們控制類的插入和移除。
想要了解關於在 JavaScript 框架中使用第三方動畫庫的更復雜的例子,請點擊下方連接:
參見 CodePen 上來自 Travis Almand(@talmand)的代碼示例:KLKdJy。
這只是一次小小的嘗試,還有更多第三方 CSS 動畫庫等你去探索。有詳細完備的,有別出心裁的,有特點鮮明的,有口味偏重的,也有直接明瞭的。還有針對複雜的 JavaScript 動畫的庫,例如 Greensock 和 Anime.js。甚至還有專門針對字母動畫的庫。
真心但願這些庫可以激發你的靈感,創造你本身的 CSS 動畫。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。