Re從零開始的UI庫編寫生活之進度條元件

FlyTeng_1874發表於2019-07-29

構想

一個網站如果想要提高使用者體驗,可以從提高使用流暢度,提高可用性,提高互動體驗等等方向入手。其中認為網站應用的流暢度對提高使用者體驗來講是很關鍵的,能夠在很大程度上對使用者的心理預期作出及時的反饋。

那麼我們就從一開始入手,去優化首屏載入的體驗。據統計,如果首屏時間在2秒以內是可以算作優秀的,5秒以內是勉強可以接受的,5秒以上的話,大部分使用者都會選擇離開。想要優化首屏時間有很多方法,可以增加伺服器資源,可以儘可能壓縮前端應用,或者部署到cdn上來縮減網站的響應時間,但無論如何,從使用者發起請求到首屏載入完成這段時間,應用都是無法使用的,而且很多方法都需要有一定的成本。那究竟有沒有一種通用的方法能夠在不需要什麼成本的情況下儘可能縮短白屏時間呢?答案是有的,就是各種載入進度條,css載入動畫和骨架屏。其中載入進度條很關鍵,除了能有效減少使用者在等待白屏是的焦慮,還能提高應用整體銜接的流暢度。能夠有效暗示使用者的,給與一個心理期待,提升主觀體驗,讓使用者更願意等待更長的時間。

想要的效果

能根據網站的載入情況顯示當前的載入進度。使用者看到的白屏時間至多等於伺服器響應時間加上index.html檔案的載入時間,這個時間一般在1秒左右,index.html一般也只有幾kb(千萬別說把什麼東西都往裡放...)。

image

嗯,Demo就是SluckyUI首屏載入時的頂部載入進度條。

整體結構

放置

為了儘可能快地看到載入效果,縮短白屏時間,相關的功能程式碼應該直接加到index.html中,這樣使用者僅僅載入完index.html就可以看到載入進度條效果,同時功能程式碼儘可能使用原生js編寫。

進度開始

我們用一個自執行函式隔離作用域來作為載入進度條的開始

// 類似於這樣
(function(){
    updateProgress()
})()
複製程式碼

進度結束

監聽瀏覽器的onload事件,當該頁面的所有資源載入完成後,瀏覽器會觸發onload事件,所以onload事件的發生可以作為整個載入過程的結束標誌。

載入過程

重點來了,應該如何儘可能真實地去記錄載入的過程呢?嗯,我們可以去逐個統計每個資源的載入情況,然後轉化為百分比顯示到進度條上。nonono,這種方法除了能真的反映真實的載入情況外,就沒什麼優點了,通用性不夠,精確去統計每個資源很麻煩等等。。。

是的,由於每個資源的大小數量不一,使用者的載入速度又有差別,所以精確地計算頁面的載入情況是一件很麻煩很頭痛的事。這個時候應該劍走偏鋒,施展一下前端的障眼法哈哈。在明確開始和結束的前提下,我們就可以對載入過程進行模擬,即高仿,一般使用者很難發現的那種高仿,會心一笑。

要做高仿的話有幾個點非常重要

  • 進度條每次前進的距離要隨機,我們稱為隨機偏移量
  • 當進度條前進到某個點之後,就要適當停下來,等待真正的載入完成,防止還沒載入完成,進度就走完的情況出現,同時也是為了防止真正的載入未完成,進度條就走完的情況發生。而這個定下來的時機當然也要隨機。
  • 雖然關鍵節點都做成了隨機,但具體的基準值需要視情況而定,每個網站不一定一樣,而且這些基礎值的隨機也要適當。

普通系列

根據上面的條件,我們寫出一個普通系列版本,各種引數都取固定值。

<!-- index.html -->
...
<progress max="100" value="0" class="progress-loading-g" id="progress_loading"></progress>
...
<script>
    (function(){
        var progress = document.getElementById('progress_loading')
        var _timer;
        
        updateProgress(100)
    
        // 載入結束後隱藏進度條
        window.onload = function() {
            progress.style.display = "none";
        }
        
        function updateProgress(target){
            // 邊界條件判斷一下
            if (target >= 100) {
                progress.value = 100;
                return;
            }
            _timer = setInterval(function() {
                // 未達到目標值時,進度進行累加
                if (progress.value < target) {
                    progress.value += 5;
                } else {
                    // 到達目標值,停止累加
                    progress.value = target;
                    clearInterval(_timer);
                }
            }, 0);
        }
    })()
</script>
...
複製程式碼

ok,並不困難,普通系列版本已經理清了大體思路,只是普通版本中那固定增加的進度很難欺騙使用者的眼睛,那應該怎麼做呢?

準備

接下來我們要為認真系列做一些準備工作。 首先,每次增加的偏移量我們要隨機。

var _interval = Math.ceil(Math.random() * 5);
複製程式碼

每次到達的目標值也要隨機,這個目標值通常在70%~90%之間,停下之後等待真正的載入完成。

// 我們得到一個[-10,10]的區間,使得進度條能在目標值(±10)範圍內停下
var isPositive = Math.floor(Math.random() * 2);
var tarOffset = Math.ceil(Math.random() * 10);
isPositive ? target += tarOffset : target -= tarOffset;
複製程式碼

onload事件觸發後要等一定時間才讓進度條消失,防止使用者載入過快時進度條過早消失。

setTimeout(function() {
    progress.style.display = "none";
}, 3000);
複製程式碼

認真系列

<!-- index.html -->
...
<progress max="100" value="0" class="progress-loading-g" id="progress_loading"></progress>
...
<script>
    (function() {
            var $ = function(id) {
                return document.getElementById(id)
            }
            var progress = $('progress_loading');
            var _timer;
            var _disTimer;

            updateProgress(80, 300);

            function updateProgress(tar, delay, callback) {
                clearInterval(_timer);
                
                // 設定臨界值
                if (tar >= 100) {
                    progress.value = 100;
                    clearInterval(_timer);
                    callback && callback();
                    return;
                }

                // 隨機化到達的目標值
                var isPositive = Math.floor(Math.random() * 2);
                var tarOffset = Math.ceil(Math.random() * 10);
                isPositive ? tar += tarOffset : tar -= tarOffset;
                
                _timer = setInterval(function() {
                    if (progress.value < tar) {
                        // 隨機化每次前進的偏移量
                        var _interval = Math.ceil(Math.random() * 5);
                        progress.value += _interval;
                    } else {
                        // 到達目標值,停止累加
                        progress.value = tar;
                        clearInterval(_timer);
                        callback && callback();
                    }
                }, delay);
            }
            // onload時間發生時即載入完成
            window.onload = function() {
                updateProgress(100, 0, function() {
                    clearTimeout(_disTimer);
                    // 防止使用者載入過快時進度條過早消失
                    _disTimer = setTimeout(function() {
                        progress.style.display = "none";
                    }, 2000);
                });
            }
        })();
</script>
...
複製程式碼

好看的樣式是必不可少的。

進度條樣式

// index.html
...
<style>
    .progress-loading-g {
        display: block;
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 2px;
        background-image: linear-gradient(to right, #4cd964, #5ac8fa, #007aff, #34aadc, #5856d6, #ff2d55);
    }

    .progress-loading-g::-moz-progress-bar {
        background-image: linear-gradient(to right, #4cd964, #5ac8fa, #007aff, #34aadc, #5856d6, #ff2d55);
    }

    .progress-loading-g::-webkit-progress-bar {
        background-image: linear-gradient(to right, #4cd964, #5ac8fa, #007aff, #34aadc, #5856d6, #ff2d55);
    }

    .progress-loading-g::-webkit-progress-value {
        background: #fff;
        transition: all 1s;
    }
</style>
...
複製程式碼

Bingo!到此為止,一個高模擬的載入進度條就搞定了,將相關功能程式碼加入你的網站就可以有效提高整體的流暢度和使用者體驗,快快行動起來吧。記得不同網站的情況不同,根據情況可以微調進度條每次前進的偏移量和間隔時間。

附:載入進度條原始碼

更多有趣的進度條

image

Demo和使用文件在載入狀態Loading標籤下

附:其他進度條原始碼

結尾

這個載入進度條的關鍵點在於站在使用者的角度去考慮真實的載入過程是怎樣的,這樣模擬效果也最好哈哈。更多有趣的元件盡在SluckyUI中,歡迎多多交流,期待你的加入。其中涉及的元件已更新在npm,npm i slucky安裝最新版本即可使用。

從零開始系列傳送門

相關文章