在作h5移動頁面,相信你們必定碰到過頁面已經打開,可是裏面的圖片還未加載出來的狀況,這種問題雖然不影響頁面的功能,可是不利於用戶體驗。拋開網速的緣由,解決這個問題有多方面的思路:最基本的,要從http請求合併,緩存管理,圖片壓縮等方面作性能優化;另外就是能夠對頁面裏用到的全部圖片作預加載的處理,當用戶打開頁面的時候不當即顯示第一屏,而是先顯示資源加載效果,等到加載完畢,再來顯示頁面的主內容,這樣就能解決那個問題。雖然這種加載效果佔用了用戶的瀏覽時間,可是咱們能夠把它作的好看有趣一點,因此也不會影響用戶體驗。本文實踐了這種想法,提供一個很是簡潔的圖片預加載組件,實現簡單,功能不弱,在作移動頁面的時候應該對你有參考價值。javascript
效果(代碼下載):css
html裏面的img標籤和css中background-imag等都會觸發瀏覽器去加載相關的圖片,可是若是這個圖片已經加載過了的話,瀏覽器就會直接使用這張已經加載好的圖片,從而可以瞬間在頁面中渲染出來。經過javascript,建立Image對象,而後把這些對象的src屬性設置成要加載的圖片地址也能觸發瀏覽器加載圖片,利用這一點就能實現圖片預加載的功能:在頁面裏首先把那些用到了相關的圖片的元素給藏掉,而後用js去加載圖片,等到全部圖片加載完畢再把藏掉的元素顯示便可。不過這僅僅是一個基本的實現思路,要完成一個功能較健壯的預加載組件,還有如下三個問題:html
1)進度問題java
因爲預加載的同時,還得作一個預加載的效果,這就須要把加載的進度實時通知到外部上下文才行。關於進度有兩個實現方式,第一是已加載的數據大小/總的數據大小,第二是已加載的文件數/總的文件數,在瀏覽器裏面,採用第一種方式是不現實的,根本沒有原生的辦法能夠作到,因此只能採用第二種。jquery
2)圖片加載失敗的問題瀏覽器
好比說有4張圖片,已經加載了50%,在加載第三張的時候出錯了,該不應將進度反饋成75%呢?答案是:應該。若是不這麼處理的話,進度永遠沒法到100%,頁面主內容就沒機會顯示了,雖然圖片加載有失敗的狀況,可是跟加載器沒有關係,也許圖片自己就不存在呢?也就是說圖片加載失敗不該該影響加載器的功能。緩存
3)圖片加載超時的問題性能優化
圖片不能加載過久,不然用戶一直停留在加載效果上看不到主內容,用戶的等待時間不可控制地延長,致使用戶體驗降低,這樣就有悖加載器的初衷了。因此應該給每一個圖片設置一個加載的超時時間,若是在全部圖片的超時時間以後,還沒加載完,就應該主動放棄加載,通知外部上下文加載完畢,顯示主內容。jquery插件
綜合以上這些需求,本文提供的實現是:grunt
(function () { function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } /** * @param imgList 要加載的圖片地址列表,['aa/asd.png','aa/xxx.png'] * @param callback 每成功加載一個圖片以後的回調,並傳入「已加載的圖片總數/要加載的圖片總數」表示進度 * @param timeout 每一個圖片加載的超時時間,默認爲5s */ var loader = function (imgList, callback, timeout) { timeout = timeout || 5000; imgList = isArray(imgList) && imgList || []; callback = typeof(callback) === 'function' && callback; var total = imgList.length, loaded = 0, imgages = [], _on = function () { loaded < total && (++loaded, callback && callback(loaded / total)); }; if (!total) { return callback && callback(1); } for (var i = 0; i < total; i++) { imgages[i] = new Image(); imgages[i].onload = imgages[i].onerror = _on; imgages[i].src = imgList[i]; } /** * 若是timeout * total時間範圍內,仍有圖片未加載出來(判斷條件是loaded < total),通知外部環境全部圖片均已加載 * 目的是避免用戶等待時間過長 */ setTimeout(function () { loaded < total && (loaded = total, callback && callback(loaded / total)); }, timeout * total); }; "function" === typeof define && define.cmd ? define(function () { return loader }) : window.imgLoader = loader; })();
使用方式(對應代碼中的test.html):
<script src="../js/imgLoader.js"></script> <script> imgLoader(['../img/page1.jpg', '../img/page2.jpg', '../img/page3.jpg'], function(percentage){ console.log(percentage) }); </script>
運行結果:
本文開篇給出的效果,對應的頁面是index.html,關於這個效果還有兩個問題須要說明:
1)它用了以前這篇博客利用輪播原理結合hammer.js實現簡潔的滑屏功能介紹的滑屏思路,並把它的一些邏輯包裝在了swipe.js,對外提供了一個全局變量Swipe,這個模塊有一個init的方法,以便外部經過調用Swipe.init()就能初始化滑屏相關的功能,原來沒有提供這個init方法,在js加載完畢就會初始化滑屏功能,有了這個init方法就能夠把滑屏的邏輯延遲到加載完畢的時候去初始化。index.html一共引用了5個js:
<script src="js/zepto.js"></script> <script src="js/transition.js"></script> <script src="js/hammer.js"></script> <script src="js/imgLoader.js"></script> <script src="js/swipe.js"></script>
其中imgLoader.js就是前面介紹圖片加載器的實現,前三個js都是爲最後一個swipe.js服務的,感興趣的能夠繼續個人博客利用輪播原理結合hammer.js實現簡潔的滑屏功能瞭解相關內容。不過滑屏不是本文的重點,不瞭解swipe.js不會影響理解本文的內容~
2)雖然我在demo中用到了3張比較大的圖片,可是因爲在本地環境,加載速度仍是很是快,因此一開始的時候,很難看到預加載的效果,最後只能想辦法在每一個進度回調以前作一下延遲,這才能夠看到前面gif圖片一開始的那個loading效果,實現方式是:
//模擬加載慢的效果 var callbacks = []; imgLoader(['img/page1.jpg', 'img/page2.jpg', 'img/page3.jpg'], function (percentage) { var i = callbacks.length; callbacks.push(function(){ setTimeout(function(){ var percentT = percentage * 100; $('#loader__info').html('Loading ' + (parseInt(percentT)) + '%'); $('#loader__progress')[0].style.width = percentT + '%'; if (percentage == 1) { setTimeout(function(){ $('#loader').remove(); Swipe.init(); }, 600); } callbacks[i + 1] && callbacks[i + 1](); },600); }); if(percentage == 1) { callbacks[0](); } });
在真實環境,最好仍是不要刻意去加這種延遲,不必爲了讓用戶看到一個好看有趣的加載效果,就浪費它沒必要要的等待時間,因此真實環境仍是應該用下面的代碼:
imgLoader(['img/page1.jpg', 'img/page2.jpg', 'img/page3.jpg'], function (percentage) { var percentT = percentage * 100; $('#loader__info').html('Loading ' + (parseInt(percentT)) + '%'); $('#loader__progress')[0].style.width = percentT + '%'; if (percentage == 1) { $('#loader').remove(); Swipe.init(); } });
另外運行demo,須要用到grunt啓動靜態服務,若是已經安裝好grunt-cli,則直接運行grunt connect任務便可打開demo的index.html。
預加載是一種比較常見的實現效果,可是在使用的時候,有些問題須要注意:
1)何時用
頁面大的時候用,通常頁面大小超過3M就該考慮使用;頁面內包含數據量比較大的圖片,在手機端測試可以明顯看到加載緩慢的時候,能夠考慮使用。
2)儘可能使用sprite圖片
3)加載效果實現的時候,儘可能不用圖片,即便要用也應該用很小的圖片,不然加載效果卡在那就沒有意義了。
本文主要介紹了一個簡單的圖片預加載器,可應用於h5移動頁面的開發當中,在它的思路之下,若是有必要的話,還能夠對它進行一些改造,用它來加載其它類型的資源,好比音頻或者視頻文件,畢竟這些類型的DOM對象也都有提供相似Image對象的屬性和回調。與預加載的方式相反的,還有一種圖片懶加載的技術,如今網上已經有比較好用的jquery插件了,不過仍是很值的去深刻了解下它的思路跟實現要點,等我有時間去研究研究再寫博客來介紹,敬請關注!