HTML动画 request animation frame
在網頁中,實現動畫無外乎兩種方式。
1. CSS3 方式,也就是利用瀏覽器對CSS3 的原生支持實現動畫;
2. 腳本方式,通過間隔一段時間用JavaScript 來修改頁面元素樣式來實現動畫。
接下來我們就分別介紹這兩種方式的原理,讓大家先對這兩種方式有一個直觀認識,了解各自的優缺點。
CSS3 的方式下,開發者一般在css 中定義一些包含CSS3 transition 語法的規則。在某些特定情況下,讓這些規則發生作用,于是瀏覽器就會將這些規則應用于指定的DOM元素上,產生動畫的效果。這種方式毫無疑問運行效率要比腳本方式高,因為瀏覽器原生支持,省去了JavaScript 的解釋執行負擔,有的瀏覽器(比如Chrome 瀏覽器)甚至還可以充分利用GPU 加速的優勢,進一步增強了動畫渲染的性能。不過CSS3 的方式并非完美,也有不少缺點。
首先, CSS3 Transition 對一個動畫規則的定義是基于時間和速度曲線( Speed Curve)的規則。換句話來說,就是CSS3 的動畫過程要描述成“在什么時間范圍內,以什么樣的運動節奏完成動畫” 。
<!DOCTYPE html> <html><head><style> .sample {background: red;position: absolute;left: 0px;width: 100px;height: 100px;transition-property: left;transition-duration: 0.5s;transition-timing-function: ease } .sample:hover {left: 420px; }</style></head><body><div class="sample" /></body> </html>
在上面的例子中, sample 類的元素定義了這樣的動畫屬性:“ left 屬性會在0.2 秒內以ease 速度曲線完成動畫” 。transition 只定義了動畫涉及的屬性、時間和速度曲線,并不定義需要修改的具體值。sample 類的left 屬性默認值為0 ,當鼠標移到sample 類元素上時, left 屬性就擁有新的值420px 。這時候transition 定義的規則發生作用,讓left 屬性以ease 速度曲線在0.2 秒
的時間完成從0 變成420px 的轉化過程,這個過程中,用戶看到的就是sample 類元素向右移動420 個像素的動畫過程。
? ? ? ? 因為CSS3 定義動畫的方式是基于時間和速度曲線,可能不利于動畫的流暢,因為動畫是可能會被中途打斷的,在上面的例子中,鼠標移到sample 類元素上的時候開始動畫,但是在0.2 秒的動畫時間內,用戶的鼠標可能會移出這個sample 類元素,這時候CSS3 還會以ease 速度曲線的節奏讓sample 類元素回到原位。從用戶體驗角度來說,中途sample 類元素回到原位的動作,語義上是“取消操作”的含義,但卻依然以同樣的時間和ease 節奏來完成“取消操作”的動畫,這并不合理。
? ? ? ? ? 時間和速度曲線的不合理是CSS3 先天的屬性,更讓開發者頭疼的就是開發CSS3 規則的過程,尤其是對transition-duration 時間很短的動畫調試,因為CSS3 的transition 過程總是一閃而過,捕捉不到中間狀態,只能一遍一遍用肉眼去檢驗動畫效果,用CSS3做過復雜動畫的開發者肯定都深有體會。雖然CSS3 有這樣一些缺點,但是因為其無與倫比的性能,用來處理一些簡單的動畫還是不錯的選擇。
? ? ? ?相對于CSS3 方式,腳本方式最大的好處就是更強的靈活度,開發者可以任意控制動畫的時間長度,也可以控制每個時間點上元素渲染出來的樣式,可以更容易做出豐富的動畫效果。腳本方式的缺點也很明顯,動畫過程通過JavaScript 實現,不是瀏覽器原生支持,消耗的計算資源更多。如果處理不當,動畫可能會出現卡頓滯后現象,本來使用動畫是為了創造更好的用戶體驗,如果出現卡頓,反而對用戶體驗帶來不好的影響。最原始的腳本方式就是利用setlnterval 或者setTimeout 來實現,每隔一段時間一個指定的函數被執行來修改界面的內容或者樣式,從而達到動畫的效果。
<!DOCTYPE html> <html><head><style> #sample {position: absolute;background: red;width: 100px;height: 100px; }</style></head><body><div id="sample" /><script type="text/javascript"> var animatedElement = document.getElementById("sample"); var left = 0; var timer; var ANIMATION_INTERVAL = 16;timer = setInterval(function() {left += 10;animatedElement.style.left = left + "px";if ( left >= 400 ) {clearInterval(timer);} }, ANIMATION_INTERVAL);</script></body> </html>
在上面的例子中,有一個常量ANIMATION INTERVAL 定義為16 , setlnterval 以這個常量為間隔,每16 毫秒計算一次sample 元素的left 值,每次都根據時間推移按比例增加left 的值,直到left 大于400 。為什么要選擇16 毫秒呢?因為每秒渲染60 幀(也叫60fps, 60 Frame Per Second)會給用戶帶來足夠流暢的視覺體驗,一秒鐘有1000 毫秒, 1000 /60 =16 ,也就是說,如果我們做到每16 毫秒去渲染一次畫面,就能夠達到比較流暢的動畫效果。對于簡單的動畫, setlnterval 方式勉強能夠及格,但是對于稍微復雜一些的動畫,腳本方式就頂不住了,比如渲染一幀要花去超過32 毫秒的時間,那么還用16 毫秒一個間隔的方式肯定不行。實際上,因為一幀渲染要占用網頁線程32 毫秒,會導致setlnterval根本無法以16 毫秒間隔調用渲染函數,這就產生了明顯的動畫滯后感,原本一秒鐘完成的動畫現在要花兩秒鐘完成,所以這種原始的setlnterval 方式是肯定不適合復雜的動畫的。
? ? ? ?出現上面問題的本質原因是setlnterval 和setTimeout 并不能保證在指定時間間隔或者延遲的情況下準時調用指定函數。所以可以換一個思路,當指定函數調用的時候,根據逝去的時間計算當前這一幀應該顯示成什么樣子,這樣即使因為瀏覽器渲染主線程忙碌導致一幀渲染時間超過16 毫秒,在后續幀誼染時至少內容不會因此滯后,即使達不倒60fps 的效果,也能保證動畫在指定時間內完成。
<!DOCTYPE html> <html><head><style> #sample {position: absolute;background: red;width: 100px;height: 100px; }</style></head><body><div id="sample" /><script type="text/javascript">var lastTimeStamp = new Date().getTime(); function raf(fn) {var currTimeStamp = new Date().getTime();var delay = Math.max(0, 16 - (currTimeStamp - lastTimeStamp));var handle = setTimeout(function(){fn(currTimeStamp);}, delay);lastTimeStamp = currTimeStamp;return handle; }var left = 0; var animatedElement = document.getElementById("sample"); var startTimestamp = new Date().getTime(); function render(timestamp) {left += (timestamp - startTimestamp) / 16;animatedElement.style.left = left + 'px';if (left < 400) {raf(render);} }raf(render);</script></body> </html>
在上面定義的raf 中,接受的fn 函數參數是真正的渲染過程, raf 只是協調渲染的節奏。raf 盡量以每隔16 毫秒的速度去調用傳遞的fn?參數,如果發現上一次被調用時間和這一次被調用時間相差不足16 毫秒,就會保持16 毫秒一次的渲染間隔繼續,如果發現
兩次調用時間間隔已經超出了16 毫秒,就會在下一次時鐘周期立刻調用fn 。上面的render 函數中根據當前時間和開始動畫的時間差來計算sample 元素的left 屬性,這樣無論render 函數何時被調用,總能夠渲染出正確的結果。
最后,我們將render 作為參數傳遞給raf ,啟動了動畫過程
轉載于:https://www.cnblogs.com/majiang/p/9691950.html
總結
以上是生活随笔為你收集整理的HTML动画 request animation frame的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 无题6
- 下一篇: 诺弗兰物语番茄面包如何制作?