關於圖片性能優化,你必須知道的
前言:其實圖片優化網上有非常多的方案,這份初探裏面做的更多的是從不同方向進行的探索~
通過一些平常可能關注不多的點,進行一堆試探,最後回到我們非常耳熟的優化方式中。
- 圖片相關概念
🌰:對於頁面進行縮放,我們並沒有改變頁面內元素的寬高 px 情況,但是縮放之後頁面元素會放大,這是通過是什麼實現的。
1.1 PX(CSS pixels)
虛擬像素,瀏覽器內的一切長度都是以 CSS 像素爲單位的,CSS 像素的單位是 px。
像素:它是圖像顯示的基本單元,既不是一個確定的物理量,也不是一個點或者小方塊,而是一個抽象概念。所以在談論像素時一定要清楚它的上下文!不同的設備,圖像基本採樣單元是不同的,顯示器上的物理像素等於顯示器的點距。
由於不同的物理設備的物理像素的大小是不一樣的,所以 css 認爲瀏覽器應該對css
中的像素進行調節,使得瀏覽器中 css
像素的大小在不同物理設備上看上去大小總是差不多 ,目的是爲了保證閱讀體驗一致。基本都是根據設備像素換算來達到這一點的。
1.2 DP(device pixels)
設備像素(物理像素),顧名思義,顯示屏是由一個個物理像素點組成的,通過控制每個像素點的顏色,使屏幕顯示出不同的圖像,屏幕從工廠出來那天起,它上面的物理像素點就固定不變了,單位 pt。pt 是一個絕對單位。
液晶顯示器只有在桌面分辨率與物理分辨率一致的情況下,顯示效果最佳,所以現在我們的桌面分辨率幾乎總是與顯示器的物理分辨率一致了。
1.3 DIP(Device independent Pixel)
設備獨立像素,也稱爲邏輯像素,簡稱 dip。
CSS 像素 = 設備獨立像素 = 邏輯像素
通過 screen.width 和 screen.height 可以獲得
1.4 DPR(devicePixelRatio)
設備像素比:設備物理像素和設備獨立像素的比例。> DPR = 設備物理像素 / 設備獨立像素 = 設備物理像素 / CSS 像素
window.devicePixelRatio ,可以看到當前的值
設備像素比 (dpr) 是指在移動開發中 1 個 css 像素佔用多少設備像素,如 2 代表 1 個 css 像素用 2x2 個設備像素來繪製。
設備像素比 (dpr),公式爲 1px = (dpr)^2 * 1dp,可以理解爲 1px 由多少個設備像素組成
1.5 PPI(pixels per inch)
每英寸像素取值,更確切的說法應該是像素密度,也就是衡量單位物理面積內擁有像素值的情況。
如何計算 ppi
每英寸的像素點(設備像素),已知屏幕分辨率和主對角線的尺寸,則 ppi 等於
以 iphone6 爲例:
1斜邊尺寸 = V(1920^2+1080^2) V代表開根號
2ppi = 斜邊尺寸/5.5
3ppi = 401ppi
4
5
所以對於屏幕來說,通過 ppi 可以很直觀的感受到屏幕細膩程度,而非分辨率。
回看 css 像素的相對性
默認情況下一個 CSS 像素應該是等於一個物理像素的寬度的,但是瀏覽器的放大 200% 的操作可以讓一個 CSS 像素等於了兩個設備像素寬度。而在高 PPI 的設備上,CSS 像素甚至在默認狀態下就相當於多個物理像素的尺寸
1.6 倍率與邏輯像素
2 倍屏、3 倍屏和 2 倍圖、3 倍圖
Retina 顯示屏:這是一種顯示技術,可以將把更多的像素點壓縮至一塊屏幕裏,從而達到更高的分辨率並提高屏幕顯示的細膩程度,這種分辨率在正常觀看距離下足以使人肉眼無法分辨其中的單獨像素。
一般將 DPR 爲 2 的屏幕稱爲 2 倍屏,DPR 爲 3 的屏幕稱爲 3 倍屏。
iphoneX 的 DPR 是 3,也就是 3 倍屏幕
面對相同主屏尺寸,不同主屏分辨率的設備,同一張 200*200px 的圖片爲何顯示的一樣?
實際像素除以倍率,就得到邏輯像素尺寸。只要兩個屏幕邏輯像素相同,它們的顯示效果就是相同的。
- 圖片佔用內存計算
[(Height in pixels) x (length in pixels) x (bit depth)] / 8 / 1024 = image size in kilobytes (KB).
色深 Color depth[1](位深 bit depth)。當涉及像素時,這個概念可以被定義爲每像素位數 bits per pixel (bpp)。
色深只是顏色表示的一個方面,它表示可以表達每個原色數量的精度;另一方面是可以表達多種顏色(色域)的範圍。顏色精度和色域的定義都是通過顏色編碼規範完成的,該規範將數字代碼值分配給顏色空間中的位置。24-bit,可以顯示 2^24=16,777,216 種色彩,近似高於肉眼所能分辨的顏色,稱爲真色彩。
在不同的設備,每像素字節數不同,這導致圖片所佔內存並不固定。但在固定設備中,圖片數量越多,尺寸越大,內存消耗就越大。通過 screen.colorDepth 可以查看對應設備的具體情況。
🌰:一張 640 * _480 像素的圖片, 設備爲 24-bit color depth,640 x 480 x 24 / 8 / 1024= 900 KB 813_275
一張圖片的體積是 172KB,但是實際佔用的內存是 1.16MB,而相同的圖片,我們使用的三倍圖佔用的內存就是 10.44MB 左右。
- 圖片性能比較
3.1 img 標籤和 canvas
3.1.1 內存佔用比較
前置思考問題:
圖片體積和佔用內存有必然聯繫嗎
同一張圖片,渲染成不同的尺寸,會影響到內存佔用嗎
同一張圖片展示一次和多次,內存會有影響嗎
img,backgorund-image 和 canvas 渲染方式有差異嗎
內存觀察方式:通過 chrome 的任務管理器,查看當前 tab 的內存佔用情況
使用資源:
對於 chorme 圖片緩存是放在 disk cache 中,以 5 張不同的圖片爲例:
(關於使用 memery cache 和 disk cache 可以參考:深入理解瀏覽器的緩存機制 [2])
關於圖片緩存佔用的內存空間大小問題:
Once an image file has been loaded, it must then be decompressed. This decompression can be a computationally complex task and take considerable time. The decompressed image will also use substantially more memory than the original.
圖片被加載後需要解碼,圖片的解碼是一個複雜耗時的過程,並且需要佔用比原始圖片還多的內存資源。
不論使用什麼渲染方式,過了很長一段時間之後瀏覽器會回收掉部分圖片緩存,根據緩存變換的大小來看,應該是將本身解碼轉化爲二進制的圖片數據清除掉了,只留下了未解碼的圖片數據,這個大小就近乎於本身圖片的體積大小了。如下分別是使用 canvas 剛渲染完畢的圖片緩存情況和過了一段時間之後的情況:
由於 canvas 是按位渲染的,在 retina 屏幕中,設置的 height 和 width 需要乘相應的 prd 才能保證清晰渲染倍圖。如果說一張三倍圖在一倍數的 canvas 中進行渲染,就會變得模糊,通過對比發現,變模糊之後,頁面的內存佔用會有所降低,但是還遠遠不到通過一倍圖直接渲染的內存使用情況。
1<!-- <canvas width=813 height=1250 id='canvas'></canvas> -->
2 <canvas width=2436 height=3750 id='canvas'></canvas>
3
4
1var imgsUrl = [
2 'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3651ad56a2a74d5c8dfa95141217224f~tplv-k3u1fbpfcp-watermark.image',
3 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1164d96dce0542b29560d0884679738c~tplv-k3u1fbpfcp-watermark.image',
4 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6cbc885f91954f418e58be759f6bc5b7~tplv-k3u1fbpfcp-watermark.image',
5 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6576d554526146e5983bd78be67c0ee4~tplv-k3u1fbpfcp-watermark.image',
6 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eacf3398b0b747e8a99278d5263c223d~tplv-k3u1fbpfcp-watermark.image'
7 ]
8 var imgs = [];
9 for (var i = 0; i < 5; i++) {
10 imgs[i] = new Image();
11 imgs[i].width = 817;
12 imgs[i].height = 375;
13 imgs[i].src = imgsUrl[i];
14 }
15 function draw() {
16 var ctx = document.getElementById('canvas').getContext('2d');
17 for (let i = 0; i < 5; i++) {
18 imgs[i].onload = function () {
19 console.log(i, imgs[i])
20 // ctx.drawImage(imgs[i], 0, 100 * 3 * i, 200 * 3, 100 * 3);
21 ctx.drawImage(imgs[i], 0, 375 * 3 * i, 812 * 3, 375 * 3);
22 // ctx.drawImage(imgs[i], 0, 375 * i, 812, 375);
23 }
24 }
25 }
26 draw();
27
28
通過 new Image() 來實現多張圖片的串行加載對於適當場景下的用戶體驗也會有一些提升。
此外,以 backgorund-image 展示和 img 標籤展示沒啥大區別,這裏不多羅列。
對比結論:
-
圖片佔用內存大小和圖片像素密切相關,和圖片自身體積關係不大(和前文所說圖片內存計算方式相符合);
-
渲染出來的圖片元素寬高(px)並不影響圖片的內存佔用;
-
本地讀取還是請求獲取,不是特別影響最後的內存佔用。
-
使用動態
new Image
的預加載圖片的方式,創建圖片,使用canvas
渲染比使用<img>
更加節省內存; -
使用 img 標籤渲染多張來源一致的圖片和渲染一張圖片的內存佔用情況近似;而多張來源不一致的圖片會佔用更高的內存;
-
使用 canvas 渲染多張圖片和單張圖片佔用內存情況似乎都一樣。。。。
(5,6 的具體原因還要進一步探索)
這裏觀察內存佔用的方式並不完全靠譜,存在一些疑惑:渲染一張圖片和渲染 5 張圖片的內存佔用差,並不是完全符合公式計算得到的內存大小。所以需要有更適當的內存觀察方式。
3.1.2 加載和繪製性能的比較
同一張 23M 大尺寸圖片,使用 img 標籤和 canvas,加載和繪製性能的比較。
直接使用 img 標籤
由於加載方式是不一樣的,直接用 img 渲染出來的 img 圖片會從上往下根據請求回來的數據逐節渲染,而用 canvas 則只能等圖片全部加載完畢之後一次性繪製,需要把 canvas 繪製放在 onload 中,否則會導致取不到圖片。所以對於大體積圖片的加載體驗來說,使用 img 反而體驗更好,避免了長時間的白屏。
相比較之下,兩者完整渲染出頁面內容的時間並沒有很大差異,由於圖片尺寸很大,在解碼上耗費了不少的時間,合理的優化圖片格式和圖片體積,對於圖片的加載有着更加重要的意義。
在使用 canvas 的渲染中,我們看不到頁面的 LPC(The Largest Contentful Paint),是因爲 “LCP” 考慮的元素類型爲:<img>
,<svg>
元素內的<image>
元素,<video>
元素
把圖片存儲在本地,拋開請求的問題,再來單獨看渲染。
Canvas
Long task 的內容是頁面的繪製
其實比較完之後發現,拿 canvas 和 img 標籤這樣對比靜態圖片渲染的意義不是很大,canvas 在會有單獨的圖層對於高性能圖片和動畫的處理會有更好的效果。
- 性能優化方向
一通比較下來,圖片渲染速度對於頁面請求和圖片解碼時間來說,並沒有那麼高的佔比。
圖片引起的內存佔用情況,如果說有多張高性能的圖片可以考慮通過 canvas 進行渲染,但是隻有單張的話就沒有那麼大的必要了,更好的方式是通過設備的具體情況來做響應式的圖片,比如使用 img 的 src,根據設備情況來渲染不同的倍圖。
1<img src="./image/oranges@1x.png" srcset="./image/oranges@2x.png 1x,./image/oranges@3x.png 2x" />
2
3
最後想要優化圖片性能,還是要從壓縮圖片體積下手,通過減少請求等待時間的方式,提供更快的展示體驗,而圖片壓縮都已有有許多完備方案,不同的圖片格式有着不同的編碼方式,相應的,圖片體積和解碼速度上也不盡相同,而這一塊,也是本篇文章沒有涉及的,希望感興趣的同學可以自行查閱瞭解一下。
參考資料
[1]
Color depth: https://en.wikipedia.org/wiki/Color_depth
[2]
深入理解瀏覽器的緩存機制: https://www.jianshu.com/p/54cc04190252
[3]
iOS 進階之頁面性能優化: https://www.cnblogs.com/iOSer1122/p/13928756.html
[4]
CSS 像素、物理像素、邏輯像素、設備像素比、PPI、Viewport: https://github.com/jawil/blog/issues/21
[5]
CSS 尺寸單位的認知: https://zhuanlan.zhihu.com/p/59210153
[6]
什麼是三倍圖?——移動端尺寸知識入門: https://zhuanlan.zhihu.com/p/34988701
[7]
響應式圖片: https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
[8]
Javascript 的 Image 對象、圖像渲染與瀏覽器內存兩三事: http://www.voidcn.com/article/p-rrwveyhj-dc.html
[9]
前端圖片優化機制: https://imweb.io/topic/568b20194c44bcc56092e415
[10]
前端圖片體積優化調研: https://bytedance.feishu.cn/docs/doccnZbagAJ5dKD60p8vLCDn2Rh#vV0kO6
[11]
Web 性能優化:圖片優化讓網站大小減少 62%: https://segmentfault.com/a/1190000018392559
[12]
Multimedia: Images: https://developer.mozilla.org/en-US/docs/Learn/Performance/Multimedia
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/iAutzDzKQ7ELG_XSGmZhEA