利用簡潔的圖片預載入組件提升h5移動頁面的用戶體驗

来源:http://www.cnblogs.com/lyzg/archive/2016/03/11/5264028.html
-Advertisement-
Play Games

在做h5移動頁面,相信大家一定碰到過頁面已經打開,但是裡面的圖片還未載入出來的情況,這種問題雖然不影響頁面的功能,但是對於不利於用戶體驗。拋開網速的原因,解決這個問題有多方面的思路:最基本的,要從http請求合併,緩存管理,圖片壓縮等方面做性能優化;另外就是可以對頁面里用到的所有圖片做預載入的處理。


在做h5移動頁面,相信大家一定碰到過頁面已經打開,但是裡面的圖片還未載入出來的情況,這種問題雖然不影響頁面的功能,但是不利於用戶體驗。拋開網速的原因,解決這個問題有多方面的思路:最基本的,要從http請求合併,緩存管理,圖片壓縮等方面做性能優化;另外就是可以對頁面里用到的所有圖片做預載入的處理,當用戶打開頁面的時候不立即顯示第一屏,而是先顯示資源載入效果,等到載入完畢,再來顯示頁面的主內容,這樣就能解決那個問題。雖然這種載入效果占用了用戶的瀏覽時間,但是我們可以把它做的好看有趣一點,所以也不會影響用戶體驗。本文實踐了這種想法,提供一個非常簡潔的圖片預載入組件,實現簡單,功能不弱,在做移動頁面的時候應該對你有參考價值。

效果(代碼下載):

loader

1. 實現思路

html裡面的img標簽和css中background-imag等都會觸發瀏覽器去載入相關的圖片,但是如果這個圖片已經載入過了的話,瀏覽器就會直接使用這張已經載入好的圖片,從而能夠瞬間在頁面中渲染出來。通過javascript,創建Image對象,然後把這些對象的src屬性設置成要載入的圖片地址也能觸發瀏覽器載入圖片,利用這一點就能實現圖片預載入的功能:在頁面里首先把那些用到了相關的圖片的元素給藏掉,然後用js去載入圖片,等到所有圖片載入完畢再把藏掉的元素顯示即可。不過這僅僅是一個基本的實現思路,要完成一個功能較健壯的預載入組件,還有以下三個問題:

1)進度問題

由於預載入的同時,還得做一個預載入的效果,這就需要把載入的進度實時通知到外部上下文才行。關於進度有兩個實現方式,第一是已載入的數據大小/總的數據大小,第二是已載入的文件數/總的文件數,在瀏覽器裡面,採用第一種方式是不現實的,根本沒有原生的辦法可以做到,所以只能採用第二種。

2)圖片載入失敗的問題

比如說有4張圖片,已經載入了50%,在載入第三張的時候出錯了,該不該將進度反饋成75%呢?答案是:應該。如果不這麼處理的話,進度永遠無法到100%,頁面主內容就沒機會顯示了,雖然圖片載入有失敗的情況,但是跟載入器沒有關係,也許圖片本身就不存在呢?也就是說圖片載入失敗不應該影響載入器的功能。

3)圖片載入超時的問題

圖片不能載入太久,否則用戶一直停留在載入效果上看不到主內容,用戶的等待時間不可控制地延長,導致用戶體驗下降,這樣就有悖載入器的初衷了。所以應該給每個圖片設置一個載入的超時時間,如果在所有圖片的超時時間之後,還沒載入完,就應該主動放棄載入,通知外部上下文載入完畢,顯示主內容。

綜合以上這些需求,本文提供的實現是:

(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>

運行結果:

image

2. demo說明

本文開篇給出的效果,對應的頁面是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();
    }
});

3. 註意事項

預載入是一種比較常見的實現效果,但是在使用的時候,有些問題需要註意:

1)什麼時候用

頁面大的時候用,一般頁面大小超過3M就該考慮使用;頁面內包含數據量比較大的圖片,在手機端測試能夠明顯看到載入緩慢的時候,可以考慮使用。

2)儘量使用sprite圖片

3)載入效果實現的時候,儘量不用圖片,即使要用也應該用很小的圖片,否則載入效果卡在那就沒有意義了。

4. 總結

本文主要介紹了一個簡單的圖片預載入器,可應用於h5移動頁面的開發當中,在它的思路之下,如果有必要的話,還可以對它進行一些改造,用它來載入其它類型的資源,比如音頻或者視頻文件,畢竟這些類型的DOM對象也都有提供類似Image對象的屬性和回調。與預載入的方式相反的,還有一種圖片懶載入的技術,現在網上已經有比較好用的jquery插件了,不過還是很值的去深入瞭解下它的思路跟實現要點,等我有時間去研究研究再寫博客來介紹,敬請關註!

本文代下載


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 可選參數和命名參數 不多說,上代碼,自然懂 class Program { static void Main(string[] args) { var troy = new Troy(); troy.HelloWorld(1);//此時b和c都為0 troy.HelloWorld(1,2);//此時
  • 回到目錄 之前寫的一篇文章,主要針對View視圖,它可以放在N級目錄下,不必須非要在views/controller/action這種關係了,而在程式運行過程中,發現分頁視圖對本功能並不支持,原因很簡單,在RazorViewEngine有不同的屬於來修飾這兩個東西,對於View的查找,通過ViewL
  • 在平時使用軟體或是.NET程式開發的過程中,我們有時會遇到程式關閉後但進程卻沒有退出的情況,這往往預示著代碼中有問題存在,不能正確的在程式退出時停止代碼執行和銷毀資源。這個現象有時並不容易被察覺,但在另一些情況下卻會產生影響軟體功能的Bug。本文列舉可能影響.NET程式進程退出的因素,並用幾個小例子
  • 求兩個數之和。這個問題夠簡單吧!能做對絕對不是問題,問題是你是否能做的比較好。好了,請看題目: Given an array of integers, return indices of the two numbers such that they add up to a specific targ
  • Controller中的數據,不管是使用的是ViewModel 還是ViewBag.Data,要將他傳遞到View中,這個很容易,但是如果要將它傳遞給JS中的某個對象,這個改如何處理呢? 後臺的數據格式: public class ViewModel { public int ID { get; s
  • 分散式dubbo zookeeperfastdfsactivemqredis 分散式緩存
  • 1 package com.shejimoshi.behavioral.TemplateMethod; 2 3 4 /** 5 * 功能:抽象模板類 6 * 時間:2016年3月10日下午9:02:32 7 * 作者:cutter_point 8 */ 9 public abstract class
  • 本節目錄 介紹 定義Filter 設置Filter 這是Abp中多租戶、軟刪除、激活\禁用等如此方便的原因 Install-Package EntityFramework.DynamicFilters 定義數據 class DemoDb : DbContext { public DemoDb() :
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...