【優(yōu)化】949- 你必須知道的圖片性能優(yōu)化方式
前言:其實(shí)圖片優(yōu)化網(wǎng)上有非常多的方案,這份初探里面做的更多的是從不同方向進(jìn)行的探索~
通過一些平??赡荜P(guān)注不多的點(diǎn),進(jìn)行一堆試探,最后回到我們非常耳熟的優(yōu)化方式中。
1. 圖片相關(guān)概念
??:對于頁面進(jìn)行縮放,我們并沒有改變頁面內(nèi)元素的寬高px情況,但是縮放之后頁面元素會放大,這是通過是什么實(shí)現(xiàn)的。
1.1 PX(CSS pixels)
虛擬像素,瀏覽器內(nèi)的一切長度都是以CSS像素為單位的,CSS像素的單位是px。
像素:它是圖像顯示的基本單元,既不是一個確定的物理量,也不是一個點(diǎn)或者小方塊,而是一個抽象概念。所以在談?wù)撓袼貢r一定要清楚它的上下文!不同的設(shè)備,圖像基本采樣單元是不同的,顯示器上的物理像素等于顯示器的點(diǎn)距。
由于不同的物理設(shè)備的物理像素的大小是不一樣的,所以css認(rèn)為瀏覽器應(yīng)該對css中的像素進(jìn)行調(diào)節(jié),使得瀏覽器中 css 像素的大小在不同物理設(shè)備上看上去大小總是差不多 ,目的是為了保證閱讀體驗(yàn)一致?;径际歉鶕?jù)設(shè)備像素換算來達(dá)到這一點(diǎn)的。
1.2 DP(device pixels)
設(shè)備像素(物理像素),顧名思義,顯示屏是由一個個物理像素點(diǎn)組成的,通過控制每個像素點(diǎn)的顏色,使屏幕顯示出不同的圖像,屏幕從工廠出來那天起,它上面的物理像素點(diǎn)就固定不變了,單位pt。pt是一個絕對單位。
液晶顯示器只有在桌面分辨率與物理分辨率一致的情況下,顯示效果最佳,所以現(xiàn)在我們的桌面分辨率幾乎總是與顯示器的物理分辨率一致了。
1.3 DIP(Device independent Pixel)
設(shè)備獨(dú)立像素,也稱為邏輯像素,簡稱dip。
CSS像素 =設(shè)備獨(dú)立像素 = 邏輯像素
通過screen.width和screen.height可以獲得
1.4 DPR(devicePixelRatio)
設(shè)備像素比:設(shè)備物理像素和設(shè)備獨(dú)立像素的比例。> DPR = 設(shè)備物理像素/設(shè)備獨(dú)立像素 = 設(shè)備物理像素/CSS像素
window.devicePixelRatio ,可以看到當(dāng)前的值
設(shè)備像素比(dpr) 是指在移動開發(fā)中1個css像素占用多少設(shè)備像素,如2代表1個css像素用2x2個設(shè)備像素來繪制。
設(shè)備像素比(dpr),公式為1px = (dpr)^2 * 1dp,可以理解為1px由多少個設(shè)備像素組成
1.5 PPI(pixels per inch)
每英寸像素取值,更確切的說法應(yīng)該是像素密度,也就是衡量單位物理面積內(nèi)擁有像素值的情況。
如何計算ppi
每英寸的像素點(diǎn)(設(shè)備像素),已知屏幕分辨率和主對角線的尺寸,則ppi等于
以iphone6為例:

斜邊尺寸 = V(1920^2+1080^2) V代表開根號
ppi = 斜邊尺寸/5.5
ppi = 401ppi
所以對于屏幕來說,通過ppi可以很直觀的感受到屏幕細(xì)膩程度,而非分辨率。
回看css像素的相對性
默認(rèn)情況下一個CSS像素應(yīng)該是等于一個物理像素的寬度的,但是瀏覽器的放大200%的操作可以讓一個CSS像素等于了兩個設(shè)備像素寬度。而在高PPI的設(shè)備上,CSS像素甚至在默認(rèn)狀態(tài)下就相當(dāng)于多個物理像素的尺寸
1.6 倍率與邏輯像素
2倍屏、3倍屏和2倍圖、3倍圖
Retina顯示屏:這是一種顯示技術(shù),可以將把更多的像素點(diǎn)壓縮至一塊屏幕里,從而達(dá)到更高的分辨率并提高屏幕顯示的細(xì)膩程度,這種分辨率在正常觀看距離下足以使人肉眼無法分辨其中的單獨(dú)像素。
一般將DPR為2的屏幕稱為2倍屏,DPR為3的屏幕稱為3倍屏。
iphoneX的DPR是3,也就是3倍屏幕
面對相同主屏尺寸,不同主屏分辨率的設(shè)備,同一張200*200px的圖片為何顯示的一樣?
實(shí)際像素除以倍率,就得到邏輯像素尺寸。只要兩個屏幕邏輯像素相同,它們的顯示效果就是相同的。
2. 圖片占用內(nèi)存計算
[(Height in pixels) x (length in pixels) x (bit depth)] / 8 / 1024 = image size in kilobytes (KB).
色深Color depth[1](位深bit depth)。當(dāng)涉及像素時,這個概念可以被定義為每像素位數(shù)bits per pixel (bpp)。
色深只是顏色表示的一個方面,它表示可以表達(dá)每個原色數(shù)量的精度;另一方面是可以表達(dá)多種顏色(色域)的范圍。顏色精度和色域的定義都是通過顏色編碼規(guī)范完成的,該規(guī)范將數(shù)字代碼值分配給顏色空間中的位置。24-bit,可以顯示2^24=16,777,216 種色彩,近似高于肉眼所能分辨的顏色,稱為真色彩。
在不同的設(shè)備,每像素字節(jié)數(shù)不同,這導(dǎo)致圖片所占內(nèi)存并不固定。但在固定設(shè)備中,圖片數(shù)量越多,尺寸越大,內(nèi)存消耗就越大。通過screen.colorDepth可以查看對應(yīng)設(shè)備的具體情況。
??:一張640 * 480像素的圖片, 設(shè)備為24-bit color depth,640 x 480 x 24 / 8 / 1024= 900 KB 813275
一張圖片的體積是172KB,但是實(shí)際占用的內(nèi)存是1.16MB,而相同的圖片,我們使用的三倍圖占用的內(nèi)存就是10.44MB左右。
3. 圖片性能比較
3.1 img標(biāo)簽和canvas
3.1.1 內(nèi)存占用比較
前置思考問題:
圖片體積和占用內(nèi)存有必然聯(lián)系嗎 同一張圖片,渲染成不同的尺寸,會影響到內(nèi)存占用嗎 同一張圖片展示一次和多次,內(nèi)存會有影響嗎 img,backgorund-image和canvas渲染方式有差異嗎
內(nèi)存觀察方式:通過chrome的任務(wù)管理器,查看當(dāng)前tab的內(nèi)存占用情況
使用資源:
給出一份十分不成熟的內(nèi)存占用情況判斷:

對于chorme圖片緩存是放在disk cache中,以5張不同的圖片為例:
圖片緩存應(yīng)該并不代算入頁面占用內(nèi)存空間?而是在disk磁盤中。
(關(guān)于使用memery cache和 disk cache 可以參考:深入理解瀏覽器的緩存機(jī)制[2])
關(guān)于圖片緩存占用的內(nèi)存空間大小問題:
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.
圖片被加載后需要解碼,圖片的解碼是一個復(fù)雜耗時的過程,并且需要占用比原始圖片還多的內(nèi)存資源。
不論使用什么渲染方式,過了很長一段時間之后瀏覽器會回收掉部分圖片緩存,根據(jù)緩存變換的大小來看,應(yīng)該是將本身解碼轉(zhuǎn)化為二進(jìn)制的圖片數(shù)據(jù)清除掉了,只留下了未解碼的圖片數(shù)據(jù),這個大小就近乎于本身圖片的體積大小了。如下分別是使用canvas剛渲染完畢的圖片緩存情況和過了一段時間之后的情況:

canvas部分的額外對比:
由于canvas是按位渲染的,在retina屏幕中,設(shè)置的height和width需要乘相應(yīng)的prd才能保證清晰渲染倍圖。如果說一張三倍圖在一倍數(shù)的canvas中進(jìn)行渲染,就會變得模糊,通過對比發(fā)現(xiàn),變模糊之后,頁面的內(nèi)存占用會有所降低,但是還遠(yuǎn)遠(yuǎn)不到通過一倍圖直接渲染的內(nèi)存使用情況。
<!-- <canvas width=813 height=1250 id='canvas'></canvas> -->
<canvas width=2436 height=3750 id='canvas'></canvas>
var imgsUrl = [
'https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3651ad56a2a74d5c8dfa95141217224f~tplv-k3u1fbpfcp-watermark.image',
'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1164d96dce0542b29560d0884679738c~tplv-k3u1fbpfcp-watermark.image',
'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6cbc885f91954f418e58be759f6bc5b7~tplv-k3u1fbpfcp-watermark.image',
'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6576d554526146e5983bd78be67c0ee4~tplv-k3u1fbpfcp-watermark.image',
'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eacf3398b0b747e8a99278d5263c223d~tplv-k3u1fbpfcp-watermark.image'
]
var imgs = [];
for (var i = 0; i < 5; i++) {
imgs[i] = new Image();
imgs[i].width = 817;
imgs[i].height = 375;
imgs[i].src = imgsUrl[i];
}
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (let i = 0; i < 5; i++) {
imgs[i].onload = function () {
console.log(i, imgs[i])
// ctx.drawImage(imgs[i], 0, 100 * 3 * i, 200 * 3, 100 * 3);
ctx.drawImage(imgs[i], 0, 375 * 3 * i, 812 * 3, 375 * 3);
// ctx.drawImage(imgs[i], 0, 375 * i, 812, 375);
}
}
}
draw();
通過new Image()來實(shí)現(xiàn)多張圖片的串行加載對于適當(dāng)場景下的用戶體驗(yàn)也會有一些提升。
此外,以backgorund-image展示和img標(biāo)簽展示沒啥大區(qū)別,這里不多羅列。
對比結(jié)論:
圖片占用內(nèi)存大小和圖片像素密切相關(guān),和圖片自身體積關(guān)系不大(和前文所說圖片內(nèi)存計算方式相符合); 渲染出來的圖片元素寬高(px)并不影響圖片的內(nèi)存占用; 本地讀取還是請求獲取,不是特別影響最后的內(nèi)存占用。 使用動態(tài) new Image的預(yù)加載圖片的方式,創(chuàng)建圖片,使用canvas渲染比使用<img>更加節(jié)省內(nèi)存;使用img標(biāo)簽渲染多張來源一致的圖片和渲染一張圖片的內(nèi)存占用情況近似;而多張來源不一致的圖片會占用更高的內(nèi)存; 使用canvas渲染多張圖片和單張圖片占用內(nèi)存情況似乎都一樣。。。。
(5,6的具體原因還要進(jìn)一步探索)
這里觀察內(nèi)存占用的方式并不完全靠譜,存在一些疑惑:渲染一張圖片和渲染5張圖片的內(nèi)存占用差,并不是完全符合公式計算得到的內(nèi)存大小。所以需要有更適當(dāng)?shù)膬?nèi)存觀察方式。
3.1.2加載和繪制性能的比較
同一張23M大尺寸圖片,使用img標(biāo)簽和canvas,加載和繪制性能的比較。
使用canvas
直接使用img標(biāo)簽

由于加載方式是不一樣的,直接用img渲染出來的img圖片會從上往下根據(jù)請求回來的數(shù)據(jù)逐節(jié)渲染,而用canvas則只能等圖片全部加載完畢之后一次性繪制,需要把canvas繪制放在onload中,否則會導(dǎo)致取不到圖片。所以對于大體積圖片的加載體驗(yàn)來說,使用img反而體驗(yàn)更好,避免了長時間的白屏。
相比較之下,兩者完整渲染出頁面內(nèi)容的時間并沒有很大差異,由于圖片尺寸很大,在解碼上耗費(fèi)了不少的時間,合理的優(yōu)化圖片格式和圖片體積,對于圖片的加載有著更加重要的意義。
在使用canvas的渲染中,我們看不到頁面的LPC(The Largest Contentful Paint),是因?yàn)椤癓CP”考慮的元素類型為:<img>,<svg>元素內(nèi)的<image>元素,<video>元素
把圖片存儲在本地,拋開請求的問題,再來單獨(dú)看渲染。
Canvas
Long task的內(nèi)容是頁面的繪制
img
其實(shí)比較完之后發(fā)現(xiàn),拿canvas和img標(biāo)簽這樣對比靜態(tài)圖片渲染的意義不是很大,canvas在會有單獨(dú)的圖層對于高性能圖片和動畫的處理會有更好的效果。
4. 性能優(yōu)化方向
一通比較下來,圖片渲染速度對于頁面請求和圖片解碼時間來說,并沒有那么高的占比。
圖片引起的內(nèi)存占用情況,如果說有多張高性能的圖片可以考慮通過canvas進(jìn)行渲染,但是只有單張的話就沒有那么大的必要了,更好的方式是通過設(shè)備的具體情況來做響應(yīng)式的圖片,比如使用img的src,根據(jù)設(shè)備情況來渲染不同的倍圖。
<img src="./image/[email protected]" srcset="./image/[email protected] 1x,./image/[email protected] 2x" />
最后想要優(yōu)化圖片性能,還是要從壓縮圖片體積下手,通過減少請求等待時間的方式,提供更快的展示體驗(yàn),而圖片壓縮都已有有許多完備方案,不同的圖片格式有著不同的編碼方式,相應(yīng)的,圖片體積和解碼速度上也不盡相同,而這一塊,也是本篇文章沒有涉及的,希望感興趣的同學(xué)可以自行查閱了解一下。
參考資料
Color depth: https://en.wikipedia.org/wiki/Color_depth
[2]深入理解瀏覽器的緩存機(jī)制: https://www.jianshu.com/p/54cc04190252
[3]iOS進(jìn)階之頁面性能優(yōu)化: https://www.cnblogs.com/iOSer1122/p/13928756.html
[4]CSS像素、物理像素、邏輯像素、設(shè)備像素比、PPI、Viewport: https://github.com/jawil/blog/issues/21
[5]CSS尺寸單位的認(rèn)知: https://zhuanlan.zhihu.com/p/59210153
[6]什么是三倍圖?——移動端尺寸知識入門: https://zhuanlan.zhihu.com/p/34988701
[7]響應(yīng)式圖片: https://developer.mozilla.org/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
[8]Javascript的Image對象、圖像渲染與瀏覽器內(nèi)存兩三事: http://www.voidcn.com/article/p-rrwveyhj-dc.html
[9]前端圖片優(yōu)化機(jī)制: https://imweb.io/topic/568b20194c44bcc56092e415
[10]前端圖片體積優(yōu)化調(diào)研: https://bytedance.feishu.cn/docs/doccnZbagAJ5dKD60p8vLCDn2Rh#vV0kO6
[11]Web 性能優(yōu)化:圖片優(yōu)化讓網(wǎng)站大小減少 62%: https://segmentfault.com/a/1190000018392559
[12]Multimedia: Images: https://developer.mozilla.org/en-US/docs/Learn/Performance/Multimedia
?? 謝謝支持
喜歡的話別忘了 分享、點(diǎn)贊、在看 三連哦~。
點(diǎn)擊下方名片,關(guān)注 前端自習(xí)課 ,獲取 更多優(yōu)秀技術(shù)文章。
