阅读 185

Re从零开始的UI库编写生活之进度条组件

构想

一个网站如果想要提高用户体验,可以从提高使用流畅度,提高可用性,提高交互体验等等方向入手。其中认为网站应用的流畅度对提高用户体验来讲是很关键的,能够在很大程度上对用户的心理预期作出及时的反馈。

那么我们就从一开始入手,去优化首屏加载的体验。据统计,如果首屏时间在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安装最新版本即可使用。

从零开始系列传送门

关注下面的标签,发现更多相似文章
评论