前端動畫實現以及原理淺析

来源:https://www.cnblogs.com/dtux/archive/2022/05/19/16287518.html
-Advertisement-
Play Games

背景 如今的前端是一個涉獵領域很廣的職業。作為一名前端,我們不僅要開發管理系統、數據中台、還要應對年報開發、節日活動等場景。不僅要會增刪改查,編寫表單,還要具備開發動畫、H5 游戲等能力。能做出很 Cool 的動畫效果,也是一種前端特有的成就感。所以,我們從動畫的實現方法入手,瞭解瀏覽器的渲染,以及 ...


背景

如今的前端是一個涉獵領域很廣的職業。作為一名前端,我們不僅要開發管理系統、數據中台、還要應對年報開發、節日活動等場景。不僅要會增刪改查,編寫表單,還要具備開發動畫、H5 游戲等能力。能做出很 Cool 的動畫效果,也是一種前端特有的成就感。所以,我們從動畫的實現方法入手,瞭解瀏覽器的渲染,以及如何提升動畫的性能。

我們先來看 2 個 H5 案例 :
【一鏡到底】

filefile<->手機掃碼體驗

【年報】

file
file
file

【其他 H5 優秀案例】
https://www.h5anli.com

第一部分 常見的動畫實現手段

1.1 gif 實現

file

定義:
GIF 文件的數據是一種基於 LZW 演算法的連續色調的無損壓縮格式,gif 格式的特點是一個 gif 文件可以存多幅彩色圖像,當數據逐幅讀出並展示都在屏幕上,就可以構成一個簡單的動畫。
最高支持 256 種顏色。由於這種特性,GIF 比較適用於色彩較少的圖片,比如頁面卡通 icon、標誌等等。

使用:

![file](https://img2022.cnblogs.com/other/2332333/202205/2332333-20220519101607717-1405608413.png)

優點:
1.製作的成本很低;
2.相容性好;
3.方便開發使用。

缺點:
1.畫質上:色彩支持少,圖像毛邊嚴重;
2.交互上:不能控制動畫的播放暫停,沒有靈活性;
3.大小上:由於是無損壓縮,每幀被完整的保存下來,導致文件很大。

1.2 css3 補幀動畫

1.2.1 transition 過渡動畫

使用:

.box {
    border: 1px solid black;
    width: 100px;
    height: 100px;
    background-color: #0000ff;
    transition: width 2s, height 2s, background-color 2s, transform 2s;
}

.box:hover {
    background-color: #ffcccc;
    width: 200px;
    height: 200px;
    transform: rotate(180deg);
}

場景:

常與 :hover, :active 等偽類使用,實現相應等動畫效果。

1.2.2 animation 關鍵幀動畫

使用:

.bounce1 {
    left: -40px;
    animation: bouncy1 1.5s infinite;
}
.bounce2 {
    left: 0;
    animation: bouncy2 1.5s infinite;
}
.bounce3 {
    left: 40px;
    animation: bouncy3 1.5s infinite;
}
@keyframes bouncy1 {
    0% {
        transform: translate(0px, 0px) rotate(0deg);
    }
    50% {
        transform: translate(0px, 0px) rotate(180deg);
    }
    100% {
        transform: translate(40px, 0px) rotate(-180deg);
    }
}

場景:
比如:loading 展示,代碼如上。

優點:
1、無需每一幀都被記錄,通過關鍵幀設置,方便開發;
2.實現簡單,通常 UI 可以直接給到 css 文件,前端只需要導入即可【移動端註意屏幕適配】。

缺點:
1.css 沒法動畫交互,無法得知當前動畫執行階段;
2.transition: 需要觸發,無法自動播放;
3.animation 相容性需要加首碼,導致代碼量成倍增長;
4.對於複雜動畫的實現,導入的 css 文件過大,影響頁面的渲染樹生成,從而阻塞渲染。比如實現一個搖錢樹的效果,css 文件達到百 kb,就要採取一些必要的壓縮手段,縮減文件大小。

1.3 js 逐幀動畫

JS 動畫的原理是通過 setTimeout 或 requestAnimationFrame 方法繪製動畫幀,從而動態地改變
網頁中圖形的顯示屬性(如 DOM 樣式,canvas 點陣圖數據,SVG 對象屬性等),進而達到動畫的目的。

demo1:
------- js 實現一個正方形從左到右的移動動畫 -----

  1. setTimeout 實現
const element2 = document.getElementById('raf2');
const btn2 = document.getElementById('btn2');
let i = 0;
let timerId;
function move () {   
    element2.style.marginLeft = i + 'px'
    timerId = setTimeout(move, 0)
    i++;
    if (i > 200) {
        clearTimeout(timerId)
    }
}
btn2.addEventListener('click',function () {
    move()
})
  1. requestAnimationFrame 實現
const element = document.getElementById('raf');
const btn1 = document.getElementById('btn1');
let r = 0;
let rafId;
function step () {
    element1.style.marginLeft = r+ 'px';
    rafId = window.requestAnimationFrame(step);
    r++;
    if (r > 200) { // 在兩秒後停止動畫
        cancelAnimationFrame(rafId);
    }
}
btn1.addEventListener('click', function () {
    step();
})

可以看出,實現的方式都是控制 dom 的 margin-left 樣式,執行動畫。

問題 1.1:demo1 中看出 setTimeout 的執行很快。這是為什麼呢?請接著往後看~

第二部分 瀏覽器如何渲染與動畫的渲染

2.1 瀏覽器的幀原理

問題 2:當 url 輸入到一個頁面展示出來經過了哪些過程?

這裡我們忽略 http 請求靜態文件之前的步驟,著重看瀏覽器渲染幀是怎麼做的,從而找到瀏覽器是如何渲染動畫的。

file

藉助 chrome-performance【執行 raf.html】 同樣可以看出上圖不同階段在 performance 裡面的標註。⚠️註意:不是每幀都總是會經過管道每個部分的處理。實際上,不管是使用 JavaScript、CSS 還是網路動畫,在實現視覺變化時,管道幀對指定幀的運行通常有三種方式:

【以下截圖是以時間線為主軸,進行繪製】

file

-當修改一些會觸發 layout 的屬性,則會導致後面的同樣被更新。

file

  • 當修改只改變 paint 的屬性, 則不會重新 layout。

file

  • 如果改一些不涉及佈局也不涉及重繪的數據,則可以直接進行合成渲染。

像 CSS 屬性具體可以查詢這個網站 ,去查閱哪些屬性會引起怎樣的幀管道:https://csstriggers.com/

例如:transform 變換,它是一個不會觸發佈局與繪製的變化的,所以使用它的時候,直接進入第三種狀態,在合成之後,直接進入 Composite 階段,是一個很好的優化手段。

file

問題 3: 控制臺上顯示出 requestAnimationFrame(rAF)的執行,那麼這個 rAF 執行與瀏覽器幀有什麼關係呢?我們接著往下看。

2.2 requestAnimationFrame 執行

我們還是運行 demo1 的代碼:

可以看到 rAF 執行在 layout 與 paint 之前,在每幀只執行了一次 rAF,調用回調函數執行動畫。

從 rAF 的執行時機,可以看出 setTimeout 的執行時機與 rAF 的不同。

我們通過對不同方式實現方塊移動動畫的 performance 抓取,可以看到:

  • setTimeout 單位幀截圖:

file

  • rAF 單位幀截圖:

file

對比兩者可以看出,在 16.7ms 的時間里,seTimeout 執行了 4 次,導致此時設置的 marginLeft 和上一次渲染前 marginLeft 的差值要大於 1px 的。

而 raf 可以看出 marginLeft 和上一次渲染前 marginLeft 的差值要等於 1px 的。

從 rAf 的性能,可以看出 setTimeout 的性能會較差一點

那麼如果 JS 執行的時間過長,導致在本該繪製一幀的時候,沒有繪製,延遲到下一幀的執行繪製的時候,就會造成動畫的卡頓。【這裡可以跳到第三部分性能問題,就知道直觀的看到卡頓】

從而可以總結出:

1.setTimeout 時間不准確,因為他的執行取決於主線程執行的時間。

2.如果計時器頻率高於瀏覽器刷新的頻率,即使代碼執行了,瀏覽器沒有刷新,也是沒有顯示的,出現掉幀情況,不流暢。

而 raf 解決了 setTimeout 動畫帶來的問題:

1.瀏覽器刷新屏幕時自動執行,無需設置時間間隔
和 setTimeout 一樣是 n 毫秒之後再執行,但這個 n 毫秒,自動設置成瀏覽器刷新頻率,瀏覽器刷新一次,執行一次,不需要手動設置;
瀏覽器不刷新,就不執行,沒有排隊掉幀的情況。

2.高頻函數節流
對於 resize、scroll 高頻觸發事件來說,使用 requestAnimationFrame 可以保證在每個繪製區間內,函數只被執行一次,節省函數執行的開銷。
如果使用 setTimeout、setInterval 可能會在瀏覽器刷新間隔中有無用的回調函數調用,浪費資源。

第三部分 性能分析以及高效能的動畫

3.1 性能分析

通過 chrome-performance 可以看整體的 fps、GPU 的情況,也可以逐幀去分析影響 scripting\rendering\painting 時間的因素,從而有針對性的提高動畫的性能。

demo3:
----- 小方塊的上下運動 -----

demo 的線上地址:https://googlechrome.github.io/devtools-samples/jank/

源碼截圖:

file

未優化【每個方塊都需要強制 layout 去計算 position】:

file

點擊 Optimize 按鈕優化後【只讀一次,並存在 pos 變數中】:

file

再次優化【添加 transform:translateZ(0),提高層級】:

file

以上就是一個動畫逐步優化的小案例:具體操作可以查看原文:
https://developer.chrome.com/docs/devtools/evaluate-performance/

3.2 如何優化動畫性能

根據上文的渲染機制的討論,我們可以看出,影響動畫渲染的因素就是幀管道所經歷的各個階段,從中我們可以總結一些用來優化動畫性能的手段:

  1. 提升每一幀的性能
  • 避免頻繁的重排
  • 避免大面積的重繪
  • 優化 JS 的性能
  1. fps 穩定,避免掉幀,跳幀的情況
  • 不在連續動畫中,添加高耗能的操作
  • 如果無法避免,看可以在動畫的開頭或者結尾進行操作
  1. 開啟 GUP 加速

第四部分 常用的動畫庫

綜上的實現方式可以支持部分的動畫開發,比如點擊交互,輪播器、以及純動畫的展示,比如搖錢樹、煙花等。

如果需要強交互,或者是需要一個重力世界的時候,原生 JS 的實現相對於困難。可以利用一些動畫庫,來進行開發,這些動畫基於 canvas 與 webGL 實現的。

  1. Pixi.js
  • 添加場景
  • 添加玩家
  • 添加自身動作
  • 添加交互
  1. phaser.js
    物理系統、重力系統
    可以模仿下落狀態

  2. 其他:
    create.js、three.js 3d 渲染、layaAir、Egret 3d 游戲引擎等,可以根據不同的場景需要,選擇不同的框架使用。

總結

  1. 動畫的實現手段

file

  1. 瀏覽器渲染的簡單流程

file

  1. 開發動畫分析性能參考 performance 的使用

鳴謝

非常感謝木杪、千尋對本文的校正與建議,同時感謝琉易、霜序等伙伴在業務產品技術上幫助與支持。


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

-Advertisement-
Play Games
更多相關文章
  • 大家好,我是大D。 今天開始給大家分享關於大數據入門技術棧——Hadoop的學習內容。 初識 Hadoop 為瞭解決大數據中海量數據的存儲與計算問題,Hadoop 提供了一套分散式系統基礎架構,核心內容包含HDFS ( Hadoop Distributed File System, 分散式文件系統) ...
  • 本文介紹什麼是 SQL 視圖,它們怎樣工作,何時使用它們。視圖提供了一種封裝 SELECT 語句的層次,可用來簡化數據處理,重新格式化或保護基礎數據。 一、視圖 視圖是虛擬的表。與包含數據的表不一樣,視圖只包含使用時動態檢索數據的查詢。 說明:SQLite 的視圖 SQLite 僅支持只讀視圖,所以 ...
  • 本文介紹如何使用 SQL CREATE TABLE 創建新表,DROP TABLE 用來完整地刪除一個表,ALTER TABLE 用來更改表列或其他諸如約束或索引等對象。 一、創建表 SQL 不僅用於表數據操縱,而且還用來執行資料庫和表的所有操作,包括表本身的創建和處理。 一般有兩種創建表的方法: ...
  • 為了助力開發者高效解決崩潰問題,HarmonyOS服務開放平臺推出了“崩潰服務能力”。你是否也好奇崩潰服務能力有哪些功能?如何集成該能力?讓我們一起往下看吧~ ...
  • 之前Content Provider,Room,DataStore一起弄,對於蒟蒻我來說步子邁得太大了,bug滿天飛(DataStore一直給我報錯說同時打開了多個DataStore,卻又找不到問題所在),遂不得不暫且拋下DataStore換回SharedPreference,後來才發現是被Hook ...
  • 5月18日晚上19點,戰“碼”先鋒第一期直播《如何成為一名優秀的OpenHamrony 貢獻者?》,在OpenHarmony社群內成功舉行。 ...
  • 現如今,視頻是用戶記錄生活最熱門的方式,各種App在發佈視頻界面都提供了視頻簡單剪輯的功能。除了增加音樂、濾鏡、貼紙這些基礎功能以外,用戶越來越追求鏡頭感,這往往需要通過專業的視頻剪輯軟體手動打上關鍵幀,進行放大、縮小、移動等操作才能實現,這增加了用戶分享視頻的門檻,不利於UGC內容的生產。 有沒有 ...
  • 1 var a,b,c,swap; 2 a=+prompt(); 3 b=+prompt(); 4 c=+prompt(); 5 //冒泡排序:最壞情況:比較n*(n-1)/2輪,以下是3個數比較,考慮最壞情況要比較3輪 6 if(a>b) 7 { 8 swap=a;//判斷表達式中:第一個值>第二 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文將以 C# 語言來實現一個簡單的布隆過濾器,為簡化說明,設計得很簡單,僅供學習使用。 感謝@時總百忙之中的指導。 布隆過濾器簡介 布隆過濾器(Bloom filter)是一種特殊的 Hash Table,能夠以較小的存儲空間較快地判斷出數據是否存在。常用於允許一定誤判率的數據過濾及防止緩存 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 「簡單有價值的事情長期堅持做」 這是成功最簡單,但也最難學的秘訣。不經過訓練,人很難意識到時間複利的威力。 仙劍奇俠傳的「十里坡劍神」和金庸群俠傳的「十級野球拳」,就是簡單的事情持之以恆反覆做,最後就有巨大的威力 唐家三少成為網文收入第一,最重要的一步是十四年從未斷日更 這樣的案例很多,一開始可能成 ...
  • 迎面走來了你的面試官,身穿格子衫,挺著啤酒肚,髮際線嚴重後移的中年男子。 手拿泡著枸杞的保溫杯,胳膊夾著MacBook,MacBook上還貼著公司標語:“我愛加班”。 面試開始,直入正題。 面試官: 看你簡歷上面寫著精通MySQL,我先問你事務的特性是什麼? 老生常談,這個還有誰不會背的嗎? 我: ...
  • 基礎知識 python是一門腳本語言,它是解釋執行的。 python使用縮進做為語法,而且python2環境下同一個py文件中不能同時存在tab和空格縮進,否則會出錯,建議在IDE中顯示縮進符。 python在聲明變數時不寫數據類型,可以type(xx)來獲取欄位的類型,然後可以int(),list ...
  • 為什麼要多線程下載 俗話說要以終為始,那麼我們首先要明確多線程下載的目標是什麼,不外乎是為了更快的下載文件。那麼問題來了,多線程下載文件相比於單線程是不是更快? 對於這個問題可以看下圖。 橫坐標是線程數,縱坐標是使用對應線程數下載對應文件時花費的時間,藍橙綠代表下載文件的大小,每個線程下載對應文件2 ...
  • 詳細講解python爬蟲代碼,爬微博搜索結果的博文數據。 爬取欄位: 頁碼、微博id、微博bid、微博作者、發佈時間、微博內容、轉發數、評論數、點贊數。 爬蟲技術: 1、requests 發送請求 2、datetime 時間格式轉換 3、jsonpath 快速解析json數據 4、re 正則表達式提... ...
  • 背景: 一般我們可以用HashMap做本地緩存,但是HashMap功能比較弱,不支持Key過期,不支持數據範圍查找等。故在此實現了一個簡易的本地緩存,取名叫fastmap。 功能: 1.支持數據過期 2.支持等值查找 3.支持範圍查找 4.支持key排序 實現思路: 1.等值查找採用HashMap2 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 本章是系列文章的第八章,用著色演算法進行寄存器的分配過程。 本文中的所有內容來自學習DCC888的學習筆記或者自己理解的整理,如需轉載請註明出處。周榮華@燧原科技 寄存器分配 寄存器分配是為程式處理的值找到存儲位置的問題 這些值可以存放到寄存器,也可以存放在記憶體中 寄存器更快,但數量有限 記憶體很多,但 ...