目前,出於性能與靈活性方面的考慮,咱們都將一些小圖片替換成矢量圖或者字體。除了這些能被替換的小圖以外,還有一些不得不使用位圖的場景,如照片、背景等。對於這些位圖,咱們須要考慮它們在加載過程當中的不一樣狀態,而制定不一樣的表現方案。javascript
圖片加載過程當中的狀態,大體包括:css
什麼時候決定加載圖片html
加載中java
加載結束,失敗的處理css3
本文將對比加載過程當中的各個狀態表現的不一樣實現方案,並對方案的簡易程度、兼容性、可擴展性進行分析。web
通常狀況,頁面加載時圖片資源做爲單獨的請求向服務器獲取。有時,因爲圖片的數量與大小的影響,這致使頁面加載緩慢,並且加載了許多用戶未觸發的圖片,也白白浪費了流量;有時,又須要保證用戶瀏覽網頁的流暢性,不得不預先加載好圖片,等用戶觸發顯示圖片。ajax
可見,正確的圖片加載時機對提升用戶體驗有很大的幫助。本小節將討論與圖片加載時機相關的技術:預加載與懶加載。瀏覽器
預加載技術就是<u>在用戶觸發圖片顯示以前,先將圖片加載到本地</u>。當用戶觸發圖片顯示的事件時,瀏覽器可以及時渲染出圖片,保證了用戶瀏覽網頁的流暢性。那麼,如何實現預加載呢?本文將介紹三種實現預加載的技術。服務器
該方法只需<u>將欲加載的圖片url寫入某個隱藏元素的background屬性中</u>,讓這張圖片和CSS文件同時加載。當用戶觸發顯示圖片的事件時,再將圖片插入到目標位置。wordpress
以下代碼所示,這裏用到CSS3的background多圖片特性,只需一個隱藏元素便可預加載全部圖片。
.nothing-1 { display: none; background: url('1.jpg'), url('2.jpg'), url('3.jpg'); }
(關於background多圖片特性的兼容性:http://caniuse.com/#search=cs... )
該方法的缺點在於沒法控制預加載的時機,只能是頁面加載時一塊兒加載圖片,若是圖片過多,會阻塞頁面的load事件,延長頁面加載時間。
第二種實現預加載的技術,是<u>直接使用JavaScript新建Image對象</u>,對其src屬性賦值。
以下代碼所示:
var image = new Image(); image.src = '...img url'; image.onload = function() { ... } image.onerror = function () { ... }
還能夠利用Image對象的load事件和error事件,完成圖片預加載和失敗以後的處理,如全部圖片加載完成後的提示等。
最後這種方法則是<u>直接發送ajax請求,獲取圖片資源</u>。一樣,可將ajax請求成功與失敗的回調,做爲圖片預加載成功與失敗的處理。
以下代碼所示(僅針對webkit瀏覽器):
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { ... } } } xhr.open("GET", '...img url'); xhr.send('');
Ajax請求方法無需多餘的Image對象,並且<u>獲取的資源也不只侷限於圖片</u>。
懶加載的核心思想是<u>根據用戶須要加載圖片資源,並不是在渲染頁面時就獲取全部圖片</u>。如此,不只能夠減輕服務器的壓力節省用戶流量,還能夠提升頁面加載速度。
首先,將頁面中全部圖片url寫進img的'data-src'屬性,並將默認的loading圖片寫入src,做爲加載中的圖片樣式。
<img src="loading.gif" data-src="...image url" />
其次,是肯定判斷圖片加載的時機,即當圖片進入瀏覽器的可視區域中時。那麼問題來了,如何判斷某個元素進入可視區域呢?
以垂直方向爲例,判斷元素是否進入可視區,首先咱們須要知道:
可視區域的高度:window.innerHeight(IE9之前不可用)|| document.documentElement.clientHeight (IE標準模式);
元素相對於可視區域頂端位置:element.getBoundingClientRect().top;
元素相對於可視區域底端位置:element.getBoundingClientRect().bottom;
判斷是否進入可視區,即<u>判斷元素的頂部相對於可視區域頂端位置是否大於0,且又小於可視區域高度;或者,元素的底部相對於可視區域底端位置是否大於0,且又小於可視區域高度</u>。
上圖中列出了元素進入可視區的全部情形,從上到下來看,第一個元素的top與bottom的值都小於0,不在可視區內;第二個元素的top小於0,而bottom卻在0到clientHeight之間,已在可視區內,須要加載;第三個元素的top與bottom都知足條件,在可視區中;剩下的兩個元素與頭兩元素狀況一致。
等到元素進入可視區域,就將img元素的'data-src'屬性賦值給'src'屬性,並標記該img元素正在加載,再也不作此處理。
以上過程僞代碼以下:
function check() { imgs.forEach(function(img, index) { if (loadedList.indexOf(index) >= 0) { return; } if (!isInClient(container)) { return; } loadedList.push(index); loadImage(img); }) }
最後,將判斷圖片進入可視區域後加載的check函數,<u>寫入window的scroll和resize事件便可</u>。
有時用戶若只想查看排序較後的圖片,會快速滾動滾動條,這種狀況下快速滾動過的圖片也沒有必要加載了。
該優化的實現思路也很簡單,<u>延遲加載邏輯,計時器一到再判斷元素是否還在可視區域</u>,可將代碼修改成以下:
function check() { imgs.forEach(function(img, index) { if (loadedList.indexOf(index) >= 0) { return; } if (!isInClient(container)) { return; } setTimeout(function() { if (!isInClient(container)) { return; } loadedList.push(index) loadImage(img) }, 1000); }) }
圖片出於加載中的狀態時,須要給用戶「圖片正在加載」的提示,圖片加載完成後,將提示隱藏。下面,本文將給出加載中提示的兩種實現方案。
此方案的實現思路是:<u>將「加載中」提示做爲img的背景,當圖片正在加載中時,頁面顯示的是背景;圖片加載完成,則覆蓋了背景顯示出圖片</u>。
關鍵代碼:
<img class="img" src="..."> .img { width: 500px; height: 300px; background: url('data:image/gif;base64,...') center no-repeat; }
這裏將「加載中」的動態圖片做爲img標籤的background,而且轉換base64編碼,這樣能夠減小一次請求。
瀏覽器兼容狀況見下表:
瀏覽器 | Chrome 54 | Safari 10.0 | Firefox 50.0 | IE 8 |
---|---|---|---|---|
兼容性 | √ | √ | √ | √ |
兼容性沒有問題,惟一的限制是須要設定.img的寬高。若事先沒法獲得圖片的寬高,則能夠在img標籤外套一層<div class="img">
,由外層容器的background做爲「加載中」提示,容器中的img可根據容器的寬或高設定其大小,關鍵代碼以下:
<div class="img"> <img src="..."> </div> img { height: 100%; }
<u>img元素的load事件會在圖片加載完成後觸發</u>,因此咱們能夠利用這一特色,進行加載中提示。
實現思路:<u>首次渲染img元素時,將「加載中」提示做爲img元素,真正的圖片則用另外一個Image對象加載。等到真正的圖片加載完成,即觸發了Image對象的load事件,則將img元素的src修改成真正圖片的url</u>。
關鍵代碼以下:
Array.from(document.getElementsByTagName('img')).forEach(img => { const image = new Image(); image.src = img.dataset.src; image.onload = () => img.src = image.src; })
這種方法須要js配合,不管是加載中的提示動畫仍是圖片均可以原始尺寸展現,而不須要再設置。
兼容性也毫無問題:
瀏覽器 | Chrome 54 | Safari 10.0 | Firefox 50.0 | IE 8 |
---|---|---|---|---|
兼容性 | √ | √ | √ | √ |
上面介紹的兩種方案都能實現「加載中」提示,方案一隻須要CSS便可,方案二須要js配合但靈活性更強。
加載中的動畫不只能夠用圖片,還能夠用CSS3的動畫特性,對於以上兩種方案稍做修改也能夠適配CSS3的加載中動畫提示:
方案一:加載動畫做爲外層div的內容,但z-index比圖片低,等圖片加載完便可覆蓋;
方案二:用js控制img和加載中動畫的顯示與隱藏便可,圖片加載完隱藏加載中動畫,顯示圖片,並可設置過渡動畫加強體驗;
圖片加載結束具備兩種結果,要麼加載成功,要麼失敗。加載成功,咱們不需多作處理,而加載失敗會出現難看的裂圖提示,影響體驗。
本文介紹兩種方法,替換圖片加載失敗的裂圖提示。
img是可替換元素,即其表現的形式與內容是被外部資源控制。當img未加載時,屬於它的:before與:after僞元素並未渲染,只有當圖片加載失敗時,這兩個僞元素纔會出現。
這裏給出加載失敗的樣式例子,CSS關鍵樣式以下:
.broken-image { width: 100%; position: relative; min-height: 50px; } .broken-image:before { content: " "; position: absolute; top: -10px; left: 0; height: calc(100% + 10px); width: 100%; background-color: rgb(230, 230, 230); border: 2px dotted rgb(200, 200, 200); border-radius: 5px; } .broken-image:after { content: "\f127" " Broken Image "; display: block; font-size: 16px; font-style: normal; font-family: FontAwesome; color: rgb(100, 100, 100); position: absolute; top: 5px; left: 0; width: 100%; text-align: center; line-height: 2; }
由於渲染後:before和:after僞元素位於img元素裏,故將img元素設爲relative,:before做爲背景,絕對定位覆蓋默認裂圖提示,:after做爲提示文字居中在img元素中。
<u>這種方法實現很方便,並且樣式可控,可是兼容性實在太差</u>,只有Chrome支持,其餘瀏覽器都沒法在圖片加載失敗後渲染出:before和:after僞元素。
翻了翻規範 https://www.w3.org/TR/CSS22/g...
This specification does not fully define the interaction of :before and :after with replaced elements (such as IMG in HTML). This will be defined in more detail in a future specification.
因此,替換元素的:before、:after僞元素由瀏覽器產商本身實現,兼容性以下:
瀏覽器 | Chrome 54 | Safari 10.0 | Firefox 50.0 | IE 8 |
---|---|---|---|---|
兼容性 | √ | ✘ | ✘ | ✘ |
與加載成功事件load類似,<u>加載失敗也會觸發對應的事件——error</u>。咱們能夠利用error事件,當加載失敗時替換img的src爲加載失敗提示圖的url。
關鍵代碼以下:
Array.from(document.getElementsByTagName('img')).forEach(img => { img.onerror = () => { img.onerror = null; img.src = 'data:image/gif;base64...'; } });
其中<u>error響應事件中要記得將img的onerror屬性置null</u>,不然一旦加載失敗提示圖也加載失敗時會陷入死循環。
該方案能夠支持當前主流瀏覽器:
瀏覽器 | Chrome 54 | Safari 10.0 | Firefox 50.0 | IE 8 |
---|---|---|---|---|
兼容性 | √ | √ | √ | √ |
以上兩種替換圖片加載失敗裂圖提示的方案,第一種因兼容性的關係只能用於特殊狀況,第二種方案須要js配合,但無兼容性問題。