背景
如今的前端是一個涉獵領域很廣的職業,作為一名前端,我們不僅要開發管理系統、資料中臺、還要應對年報開發、節榷訓動等場景,不僅要會增刪改查,撰寫表單,還要具備開發影片、H5 游戲等能力,能做出很 Cool 的影片效果,也是一種前端特有的成就感,所以,我們從影片的實作方法入手,了解瀏覽器的渲染,以及如何提升影片的性能,
我們先來看 2 個 H5 案例 :
【一鏡到底】

<->手機掃碼體驗
【年報】



【其他 H5 優秀案例】
https://www.h5anli.com
第一部分 常見的影片實作手段
1.1 gif 實作

定義:
GIF 檔案的資料是一種基于 LZW 演算法的連續色調的無損壓縮格式,gif 格式的特點是一個 gif 檔案可以存多幅彩色影像,當資料逐幅讀出并展示都在螢屏上,就可以構成一個簡單的影片,
最高支持 256 種顏色,由于這種特性,GIF 比較適用于色彩較少的圖片,比如頁面卡通 icon、標志等等,
使用:

優點:
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 實作一個正方形從左到右的移動影片 -----
- 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()
})
- 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 請求靜態檔案之前的步驟,著重看瀏覽器渲染幀是怎么做的,從而找到瀏覽器是如何渲染影片的,

借助 chrome-performance【執行 raf.html】 同樣可以看出上圖不同階段在 performance 里面的標注,??注意:不是每幀都總是會經過管道每個部分的處理,實際上,不管是使用 JavaScript、CSS 還是網路影片,在實作視覺變化時,管道幀對指定幀的運行通常有三種方式:
【以下截圖是以時間線為主軸,進行繪制】

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

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

- 如果改一些不涉及布局也不涉及重繪的資料,則可以直接進行合成渲染,
像 CSS 屬性具體可以查詢這個網站 ,去查閱哪些屬性會引起怎樣的幀管道:https://csstriggers.com/
例如:transform 變換,它是一個不會觸發布局與繪制的變化的,所以使用它的時候,直接進入第三種狀態,在合成之后,直接進入 Composite 階段,是一個很好的優化手段,

問題 3: 控制臺上顯示出 requestAnimationFrame(rAF)的執行,那么這個 rAF 執行與瀏覽器幀有什么關系呢?我們接著往下看,
2.2 requestAnimationFrame 執行
我們還是運行 demo1 的代碼:
可以看到 rAF 執行在 layout 與 paint 之前,在每幀只執行了一次 rAF,呼叫回呼函式執行影片,
從 rAF 的執行時機,可以看出 setTimeout 的執行時機與 rAF 的不同,
我們通過對不同方式實作方塊移動影片的 performance 抓取,可以看到:
- setTimeout 單位幀截圖:

- rAF 單位幀截圖:

對比兩者可以看出,在 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/
原始碼截圖:

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

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

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

以上就是一個影片逐步優化的小案例:具體操作可以查看原文:
https://developer.chrome.com/docs/devtools/evaluate-performance/
3.2 如何優化影片性能
根據上文的渲染機制的討論,我們可以看出,影響影片渲染的因素就是幀管道所經歷的各個階段,從中我們可以總結一些用來優化影片性能的手段:
- 提升每一幀的性能
- 避免頻繁的重排
- 避免大面積的重繪
- 優化 JS 的性能
- fps 穩定,避免掉幀,跳幀的情況
- 不在連續影片中,添加高耗能的操作
- 如果無法避免,看可以在影片的開頭或者結尾進行操作
- 開啟 GUP 加速
第四部分 常用的影片庫
綜上的實作方式可以支持部分的影片開發,比如點擊互動,輪播器、以及純影片的展示,比如搖錢樹、煙花等,
如果需要強互動,或者是需要一個重力世界的時候,原生 JS 的實作相對于困難,可以利用一些影片庫,來進行開發,這些影片基于 canvas 與 webGL 實作的,
- Pixi.js
- 添加場景
- 添加玩家
- 添加自身動作
- 添加互動
-
phaser.js
物理系統、重力系統
可以模仿下落狀態 -
其他:
create.js、three.js 3d 渲染、layaAir、Egret 3d 游戲引擎等,可以根據不同的場景需要,選擇不同的框架使用,
總結
- 影片的實作手段

- 瀏覽器渲染的簡單流程

- 開發影片分析性能參考 performance 的使用
鳴謝
非常感謝木杪、千尋對本文的校正與建議,同時感謝琉易、霜序等伙伴在業務產品技術上幫助與支持,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/498841.html
標籤:HTML5
上一篇:部分Html元素匯總
下一篇:前端周刊:2022-8 期
