由奧迪車燈想到的前端動畫
最近對汽車比較感興趣,平時也多留意看了一些身邊的車,發現奧迪部分車型的轉向燈很有特色,有一個從左到右的動畫效果,視覺效果很贊,這撩起了我的好奇心,怎麼用程式碼在網頁上模擬實現呢?
先來看看我們需要實現的效果:預覽
方法一:css
首先,想到的自然是css,因為現在css動畫功能很強大,且這種方式程式碼簡單,效果順滑,強烈推薦。程式碼如下:
<div class="css-light"></div>
div.css-light { width: 0%; height: 6px; background: #ff8908; animation: mymove 1s ease-out infinite; } @keyframes mymove { from { width: 0%; } to { width: 100%; } }
其中,核心屬性是animation。那animation怎麼用呢?如下:
第一個屬性指定要繫結到選擇器的關鍵幀的名稱,本例是mymove;
第二個屬性指定動畫指定需要多少秒或毫秒完成,本例是1s;
第三個屬性設定動畫將如何完成一個週期,預設是ease,本例是ease-out,表示動畫以低速結束;
第四個屬性定義動畫的播放次數,本例是無限迴圈;
由此可見,css的animation是真的強大,還支援多種動畫效果,比如ease-out這種效果,但你要用js模擬,那可就不是一行程式碼的事兒了。
方法二:js方法
除開css,js也給與了模擬動畫的能力,這種方式通常使用setInterval()
方法來控制動畫,程式碼如下:
<div id="process"></div>
let div = document.getElementById('process') function process() { div.style.width = '0%' setInterval(() => { div.style.width = (parseInt(div.style.width, 10) + 1) + '%' if (div.style.width === '100%') { div.style.width = '0%' } }, 20) } process()
但這種方法有幾個弊端。
第一個就是不十分精確,為它傳入的第二個引數(如本例中的20),實際上只是指定了把動畫程式碼新增到瀏覽器 UI 執行緒佇列中以等待執行的時間。如果佇列前面已經加入了其他任務,那動畫程式碼就要等前面的任務完成後再執行。也就是說,以毫秒錶示的延遲時間並不代表到時候一定會執行動畫程式碼,而僅代表到時候會把程式碼新增到任務佇列中。如果 UI 執行緒繁忙,比如忙於處理使用者操作,那麼即使把程式碼加入佇列也不會立即執行。
第二個弊端就是編寫動畫迴圈需要知道延遲時間多長合適。一方面,迴圈間隔必須足夠短,這樣才能讓不同的動畫效果顯得平滑流暢;另一方面,迴圈間隔還要足夠長,這樣才能確保瀏覽器有能力渲染產生的變化。拿本例來說,如果將20改成200,那麼你會發現動畫效果會顯得一卡一卡的。而由20改成1,那麼動畫會執行的非常快,並且效能的開銷也會增加。
第三個弊端是setInterval它的開銷也比較大,即便頁面後臺執行,這個開銷也一直存在。
那麼,這些個弊端能不能優化呢?這就要用到requestAnimationFrame
了!
與setInterval相比,requestAnimationFrame最大的優勢是由系統來決定回撥函式的執行時機。
具體來說,如果螢幕重新整理率是60Hz,那麼回撥函式就每16.7ms被執行一次,如果重新整理率是75Hz,那麼這個時間間隔就變成了1000/75=13.3ms,換句話說就是,requestAnimationFrame的步伐跟著系統的重新整理步伐走。它能保證回撥函式在螢幕每一次的重新整理間隔中只被執行一次,這樣就不會引起丟幀現象,也不會導致動畫出現卡頓的問題。省心吧,用了它,你就再也不用糾結到底迴圈間隔設定為多少合適了,它幫你幹了這事。
它的用法很簡單,用requestAnimationFrame方式改寫如下:
function updateProgress () { let div = document.getElementById('process') div.style.width = (parseInt(div.style.width, 10) + 1) + '%' if (div.style.width === '100%') { div.style.width = '0%' } window.requestAnimationFrame(updateProgress); } window.requestAnimationFrame(updateProgress);
總結
本文模擬了一個簡單的車燈動畫效果,從中引出了一些前端動畫方面的知識,並用二種不同的方式進行了實現(css和js)。
最後說下,在汽車上真實的效果應該是由多個led燈來實現的,有興趣的同學可以自行模擬和拓展。