HTML動畫 request animation frame

来源:https://www.cnblogs.com/majiang/archive/2018/09/23/9691950.html
-Advertisement-
Play Games

在網頁中,實現動畫無外乎兩種方式。1. CSS3 方式,也就是利用瀏覽器對CSS3 的原生支持實現動畫;2. 腳本方式,通過間隔一段時間用JavaScript 來修改頁面元素樣式來實現動畫。接下來我們就分別介紹這兩種方式的原理,讓大家先對這兩種方式有一個直觀認識,瞭解各自的優缺點。 CSS3 的方式 ...


在網頁中,實現動畫無外乎兩種方式。
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 ,啟動了動畫過程


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 由於之前想看看.class文件中的內容是否是“0101”二進位,選擇了用記事本打開,並忘記取消勾選“始終使用選擇的程式打開這種文件(A)”,導致電腦上所有.class結尾的文件圖標和打開方式都變成了記事本,雖然好像沒什麼影響,但是經不住自己有強迫症。非要把.class文件還原成之前的狀態。用上一篇文 ...
  • Zookeeper作為一個分散式協調系統提供了一項基本服務: 分散式鎖服務 ,分散式鎖是分散式協調技術實現的核心內容。像配置管理、任務分發、組服務、分散式消息隊列、分散式通知/協調等,這些應用實際上都是基於這項基礎服務由用戶自己摸索出來的。 1.Zookeeper在大數據系統中的常見應用 zooke ...
  • 剛接觸數據倉庫是在我的第一份實習工作——數據中心數據管理系統開發,它是一個B/S架構的應用,與一般的項目不同的是,系統是以數據倉庫來進行數據存取的,這是我第一次聽說數倉這個詞,感覺它龐大而且神秘,不知道從何入手,對數據倉庫有一種敬畏之心,後來經過慢慢的學習和使用,發現其實它在應用開發中的使用方法跟傳... ...
  • 本系列博客主要介紹MySQL資料庫的binlog日誌的相關內容,這個系列的主題包括: MySQLbinlog日誌01binlog日誌基本操作 MySQLbinlog日誌02binlog日誌用於數據恢復 MySQLbinlog日誌03binlog日誌位元組碼解析 MySQLbinlog日誌04binlo ...
  • 本系列博客主要介紹MySQL資料庫的binlog日誌的相關內容,這個系列的主題包括: MySQLbinlog日誌01binlog日誌基本操作 MySQLbinlog日誌02binlog日誌用於數據恢復 MySQLbinlog日誌03binlog日誌位元組碼解析 MySQLbinlog日誌04binlo ...
  • 1.前言 總是聊併發的話題,聊到大家都免疫了,所以這次串講下個話題——資料庫(歡迎糾正補充) 看完問自己一個問題來自我檢測: NoSQL我到底該怎麼選? 1.1.分類 主要有這麼三大類:[再老的資料庫就不說了] 1.傳統資料庫(SQL): 關係資料庫:SQLite、MySQL、SQLServer.. ...
  • USE MASTER GO DECLARE @dbname SYSNAME SET @dbname = 'databasename' --這個是要刪除的資料庫庫名 DECLARE @s NVARCHAR(1000) DECLARE tb CURSOR LOCAL FOR SELECT s = 'ki ...
  • 很多人在安裝orcl資料庫時,出現很多報錯,我也不例外,因上次資料庫出現問題,無法修複,只能從新安裝,無奈的是,安裝時報啟動服務出現錯誤,找不到OracleMTSRecoveryService錯MMP,心想又是咋咯回事,第一反應肯定就是沒卸載乾凈。下麵時處理辦法:不是所有報錯通用辦法。 打開註冊表看 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...