早期在Web頁面或Web應用中實現 Web動畫 通常是使用JavaScript來完成。愛掏網 - it200.com使用JavaScript創建動畫非常靈活,但不能輕易地讓瀏覽器通過硬件加速來優化動畫,也不能將其連接到 布局 和 渲染 管道中。愛掏網 - it200.com值得慶幸的是,自2007年Webkit團隊引入的 CSS Animation 和 CSS Transition 克服了早期JavaScript動畫實現的挑戰。愛掏網 - it200.com但是,CSS Animation和Transition也有很多的限制,特別是在 動態創建動畫、控制動畫的回放和監視動畫生命周期方面等。愛掏網 - it200.com不過,Web Animation API的出現,讓開發者(特別是Web動畫方面的開發者)看到了曙光。愛掏網 - it200.com因為,Web Animation API引入了一種新的解決方案,它提供了CSS Animation和Transition的優化能力,同時還提供了早期基于JavaScript制作動畫的API的靈活性。愛掏網 - it200.comWeb Animation API通過 計時模型(Timing Mode)和動畫模型(Animation Model)提供對Web動畫開發和控制。愛掏網 - it200.com
隨著iOS 13.4、iPadOS 13.4和Safari 13.1在macOS Catalina 10.15.4中的發布,Web Animation API得到了所有主流瀏覽器的支持,也就是說,我們可以在Web動畫的開發中大膽的使用該技術了。愛掏網 - it200.com
只不過現在前端開發都依賴于主流的JavaScript框架進行開發,比如React、Vue等。愛掏網 - it200.com如果你在React或Vue中開發Web動畫的話,你會發現處理動畫的方式也會有所不同。愛掏網 - it200.com比如說,在Vue中有內置的
和
組件,允許使用CSS和JavaScript鉤子處理動畫;如果使用React,那么對于ReactCSSTransitionGroup
一定不會感到陌生,而且在React中還有很多優秀的庫用來實現Web動畫,比如 React-Motion 和 React-Gsap-Enhancer。愛掏網 - it200.com那么在這篇文章中,將和大家一起探討一下在React中如何使用React的鉤子函數和Web Animation API結合起來創建一個高性能的動效。愛掏網 - it200.com
因為我們后面的內容會涉及到React的Hooks相關的知識,如果你從未接觸過的話,建議你花點時間閱讀:
- React Hooks官方文檔
- The Guide to Learning React Hooks
- What are React Hooks?
Webkit團隊早在2007年就提出了CSS Animation 和 Transition的原始提案,經過多年發展,這些規范已經成熟并成為W3C標準和Web平臺不可或缺的一部分。愛掏網 - it200.com
有了這些技術,在Web開發中集成動畫變得很簡單,開發人員不再需要編寫JavaScript,同時允許瀏覽器在渲染動畫時啟動硬件加速(3D加速),并在布局和渲染管道中集成動畫,從而提供更好的性能。愛掏網 - it200.com
雖然CSS Animation和Transition都能實現Web動效,但他們有著明顯的區別:
簡單地說:
- CSS的
transition
只有兩個狀態:開始狀態 和 結束狀態;但animation
可能是多個狀態,有幀的概念 - CSS的
transition
需要借助別的方式來觸發,比如CSS的狀態選擇器(如:hover
)或 借助JavaScript來觸發;animation
可以自動觸發
用一個真正的示例來展示兩者的區別:
正如前面所言,可以使用animation
和transition
制作Web動畫,不過使用animation
制作Web動畫的場景更多。愛掏網 - it200.com也正因為如此,業內有一個使用animation
制作Web動畫的庫,即@Daniel Eden的 Animate.css。愛掏網 - it200.com
這個動畫庫內置了很多動畫效果。愛掏網 - it200.com
為什么要特別提到這個動畫庫呢?那是因為我們后面的內容將會用到這個庫。愛掏網 - it200.com
作為一名Web開發人員,我很喜歡CSS Animation和Transition的簡單性和卓越性能,而且我也一直在探討這方面的技術。愛掏網 - it200.com在一直以來的學習和探討當中,CSS Animation 和 Transition的這些優勢使得Web動畫成為Web開發人員的強大工具;但在日常的學習和開發過程中,我也發現了這些技術也存在一定的缺陷:動態創建、回放控制和監控動畫的生命周期!
不過值得慶幸的是,Web Animation API(簡稱 WAAPI)的出現可以解決上述提到的這些缺陷。愛掏網 - it200.com
我們先來看看WAAPI的基礎操作。愛掏網 - it200.com
使用CSS的animation
創建動畫,首先會先使用@keyframes
創建一個動畫,然后在需要使用這個動畫的元素(對象)上通過animation
屬性來調用,比如上面的示例:
@keyframes boxScale {
to {
transform: scale(1.5, 1.5);
}
}
.box {
transform-origin: center;
animation: boxScale 2s linear infinite alternate;
}
.box
元素是先放大,再回到初始大小,再放大,再回到初始大小,一直重復這樣的過程:
對于這樣的一個效果,如果我們使用WAAPI來實現的話,將會像下面這樣:
const aniElement = document.querySelector(".waapi");
const keyframes = [
{ transform: "scale(1, 1)" },
{ transform: "scale(1.5, 1.5)" }
];
const options = {
duration: 2000,
iterations: Infinity,
easing: "linear",
direction: "alternate"
};
aniElement.animate(keyframes, options);
效果如下:
從效果上來看,他們是相同的。愛掏網 - it200.com但熟悉CSS Animation的開發者都知道,CSS允許你很容易地將狀態變化(比如上示中的圓變大變大)動畫化,但如果給定動畫的開始值和結束值事先不知道,那么就會非常棘手。愛掏網 - it200.com針對于這種情況,Web開發者會用CSS Transition來處理這些情況:
// 設置transition屬性的初始值
aniElement.style.transitionProperty = 'transform'
aniElement.style.transitionDuration = '2s'
aniElement.style.transform = 'scale(1, 1)'
// 現在,設置transition屬性的結束值
aniElement.style.transform = 'scale(1.5, 1.5)'
我們可能通過事件的操作,即將最終值放到對應的事件中:
play.addEventListener("click", () => {
aniElement.style.transform = "scale(1.5, 1.5)";
});
reset.addEventListener("click", () => {
aniElement.style.transform = "scale(1, 1)";
});
效果如下:
雖然說,這樣能讓元素動起來。愛掏網 - it200.com但瀏覽器不會在它認為最合適的時候讓元素動起來。愛掏網 - it200.com比如說,如果頁面的另一部分也需要創建一個類似的動畫,那么我們將要不斷的重復這樣的代碼,這將增加了代碼量而且也有可能降低Web性能。愛掏網 - it200.com當然,你也可有會考慮使用CSS Animation來替代(即,首先@keyframes
創建一個動畫),并將其插入到或
.css
中,從而無法封裝單個元素的真正目標樣式更改,并導致昂貴的樣式無效。愛掏網 - it200.com
不過,我們改用Web Animation API,就能輕易讓瀏覽器引擎高效地運行動畫,同時還能更好的控制動畫。愛掏網 - it200.com正如上面的示例所示,我們可以使用Element.animate()
調用一個方法來重寫上面的代碼:
element.animate({
transform: [
'scale(1, 1)',
'scale(1.5, 1.5)'
]
}, 2000)
這是一個很簡單的示例,但可以說Element.animate()
方法是名副其實的瑞士軍刀,它具有更高級的特性。愛掏網 - it200.comElement.animate()
方法接受兩個參數,第一個參數指定是類似于@keyframes
動畫值,第二個參數指定的指定動畫的特性的相關參數(類似于animation-timing-function
、animation-duration
、animation-fill-mode
等)。愛掏網 - it200.com這樣一來,我們可以添加更多的參數,讓上面的動畫變得更強大:
const aniElement = document.querySelector(".box");
const play = document.getElementById("play");
const reset = document.getElementById("reset");
play.addEventListener("click", () => {
aniElement.animate(
{
transform: ["scale(1, 1)", "scale(1.5, 1.5)"]
},
{
duration: 2000,
fill: "both"
}
);
});
reset.addEventListener("click", () => {
aniElement.animate(
{
transform: ["scale(1.5, 1.5)", "scale(1, 1)"]
},
{
duration: 2000,
fill: "both"
}
);
});
效果如下:
包月會員查看