专栏名称: 程序员大咖
为程序员提供最优质的博文、最精彩的讨论、最实用的开发资源;提供最新最全的编程学习资料:PHP、Objective-C、Java、Swift、C/C++函数库、.NET Framework类库、J2SE API等等。并不定期奉送各种福利。
目录
相关文章推荐
程序员的那些事  ·  字节跳动:辞退 353 人,追刑责 39 人 ·  2 天前  
码农翻身  ·  干货!服务器运维极简指南 ·  昨天  
待字闺中  ·  给MCP祛魅 ·  4 天前  
待字闺中  ·  OpenAI给Manus祛魅 ·  3 天前  
51好读  ›  专栏  ›  程序员大咖

写一个网页进度 loading

程序员大咖  · 公众号  · 程序员  · 2017-03-17 19:31

正文

来自:简书

作者:jack_lo

www.jianshu.com/p/4c93f5bd9861


loading随处可见,比如一个app经常会有下拉刷新,上拉加载的功能,在刷新和加载的过程中为了让用户感知到 load 的过程,我们会使用一些过渡动画来表达。最常见的比如“转圈圈”,“省略号”等等。


网页loading有很多用处,比如页面的加载进度,数据的加载过程等等,数据的加载loading很好做,只需要在加载数据之前(before ajax)显示loading效果,在数据返回之后(ajax completed)结束loading效果,就可以了。


但是页面的加载进度,需要一点技巧。


页面加载进度一直以来都是一个常见而又晦涩的需求,常见是因为它在某些“重”网页(特别是网页游戏)的应用特别重要;晦涩是因为web的特性,各种零散资源决定它很难是“真实”的进度,只能是一种“假”的进度,至少在逻辑代码加载完成之前,我们都不能统计到进度,而逻辑代码自身的进度也无法统计。另外,我们不可能监控到所有资源的加载情况。


所以页面的加载进度都是“假”的,它存在的目的是为了提高用户体验,使用户不至于在打开页面之后长时间面对一片空白,导致用户流失。


既然是“假”的,我们就要做到“仿真”才有用。仿真是有意义的,事实上用户并不在乎某一刻你是不是真的加载到了百分之几,他只关心你还要load多久。 所以接下来我们就来


01 实现一个页面加载进度loading


首先准备一段loading的html:




    写一个网页进度loading


  
   
0%
 

来点样式装扮一下:

.loading {
  display: table;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #fff;
  z-index: 5;
}

.loading .progress {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}

我们先假设这个loading只需要在页面加载完成之后隐藏,中间不需要显示进度。那么很简单,我们第一时间想到的就是window.onload:


(以下内容为了方便演示,默认使用jQuery,语法有es6的箭头函数)

var $loading = $('#loading')
var $progress = $('#progress')

window.onload = () => {
  $loading.hide()
}

ok,这样基本的loading流程就有了,我们增加一个进度的效果,每隔100ms就自增1,一直到100%为止,而另一方面window loaded的时候,我们把loading给隐藏。


我们来补充一下进度:

var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0  // 初始化进度

var timer = window.setInterval(() => {  // 设置定时器  if (prg >= 100) {  // 到达终点,关闭定时器
    window.clearInterval(timer)
    prg = 100
  } else {  // 未到终点,进度自增
    prg++
  }

  $progress.html(prg + '%')
  console.log(prg)
}, 100)

window.onload = () => {
  $loading.hide()
}

效果不错,但是有个问题, 万一window loaded太慢了,导致进度显示load到100%了,loading还没有隐藏,那就打脸了 。所以,我们需要让loading在window loaded的时候才到达终点,在这之前,loading可以保持一个等待的状态,比如在80%的时候,先停一停,然后在loaded的时候快速将进度推至100%。这个做法是目前绝大部份进度条的做法。



02 让loading保持等待状态

var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0var timer = window.setInterval(() => {  if (prg >= 80) {  // 到达第一阶段80%,关闭定时器,保持等待
    window.clearInterval(timer)
    prg = 100
  } else {
    prg++
  }

  $progress.html(prg + '%')
  console.log(prg)
}, 100)

window.onload = () => {
  window.clearInterval(timer)
  window.setInterval(() => {    if (prg >= 100) {  // 到达终点,关闭定时器
      window.clearInterval(timer)
      prg = 100
      $loading.hide()
    } else {
      prg++
    }

    $progress.html(prg + '%')
    console.log(prg)
  }, 10)  // 时间间隔缩短
}

ok,这差不多就是我们想要的功能了,我们来提炼一下代码,把重复的代码给封装一下:

var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0var timer = 0progress(80, 100)

window.onload = () => {
  progress(100, 10, () => {
    $loading.hide()
  })
}

function progress (dist, delay, callback) {
  window.clearInterval(timer)
  timer = window.setInterval(() => {    if (prg >= dist) {
      window.clearInterval(timer)
      prg = dist
      callback && callback()
    } else {
      prg++
    }

    $progress.html(prg + '%')
    console.log(prg)
  }, delay)
}

我们得到了一个progress函数,这个函数就是我们主要的功能模块,通过传入一个目标值、一个时间间隔,就可以模拟进度的演化过程。


目前来看,这个进度还是有些问题的:

  1. 进度太平均,相同的时间间隔,相同的增量,不符合网络环境的特点;

  2. window.onload太快,我们还来不及看清100%,loading就已经不见了;

  3. 每次第一阶段都是在80%就暂停了,露馅儿了;


第一个点,我们要让时间间隔随机,增量也随机;第二个点很简单,我们延迟一下就好了;第三点也需要我们随机产生一个初始值。


增量随机很好办,如何让时间间隔随机? setInterval是无法动态设置delay的,那么我们就要把它改造一下,使用setTimeout来实现。(setInterval跟setTimeout的用法和区别就不细说了吧?)


03 让时间间隔随机

var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0var timer = 0progress([80, 90], [1, 3], 100)  // 使用数组来表示随机数的区间

window.onload = () => {
  progress(100, [1, 5], 10, () => {
    window.setTimeout(() => {  // 延迟了一秒再隐藏loading
      $loading.hide()
    }, 1000)
  })
}

function progress (dist, speed, delay, callback) {
  var _dist = random(dist)
  var _delay = random(delay)
  var _speed = random(speed)
  window.clearTimeout(timer)
  timer = window.setTimeout(() => {    if (prg + _speed >= _dist) {
      window.clearTimeout(timer)
      prg = _dist
      callback && callback()
    } else {
      prg += _speed
      progress (_dist, speed, delay, callback)
    }

    $progress.html(parseInt(prg) + '%')  // 留意,由于已经不是自增1,所以这里要取整
    console.log(prg)
  }, _delay)
}

function random (n) {  if (typeof n === 'object') {
    var times = n[1] - n[0]
    var offset = n[0]    return Math.random() * times + offset
  } else {    return n
  }
}

至此,我们差不多完成了需求。


but,还有一个比较隐蔽的问题,我们现在使用window.onload,发现从进入页面,到window.onload这中间相隔时间十分短,我们基本是感受不到第一阶段进度(80%)的,这是没有问题的——我们在意的是,如果页面的加载资源数量很多,体积很大的时候,从进入页面,到window.onload就不是这么快速了,这中间可能会很漫长(5~20秒不等),但事实上,我们只需要为 首屏资源 的加载争取时间就可以了,不需要等待所有资源就绪,而且更快地呈现页面也是提高用户体验的关键。


我们应该考虑页面loading停留过久的情况,我们需要为loading设置一个超时时间,超过这个时间 ,假设window.onload还没有完成,我们也要把进度推到100%,把loading结束掉。



04 超时时间设置

var $loading = $('#loading'






请到「今天看啥」查看全文