做者:jack_lojavascript
www.jianshu.com/p/4c93f5bd9861css
loading隨處可見,好比一個app常常會有下拉刷新,上拉加載的功能,在刷新和加載的過程當中爲了讓用戶感知到 load 的過程,咱們會使用一些過渡動畫來表達。最多見的好比「轉圈圈」,「省略號」等等。java
網頁loading有不少用處,好比頁面的加載進度,數據的加載過程等等,數據的加載loading很好作,只須要在加載數據以前(before ajax)顯示loading效果,在數據返回以後(ajax completed)結束loading效果,就能夠了。git
可是頁面的加載進度,須要一點技巧。es6
頁面加載進度一直以來都是一個常見而又晦澀的需求,常見是由於它在某些「重」網頁(特別是網頁遊戲)的應用特別重要;晦澀是由於web的特性,各類零散資源決定它很難是「真實」的進度,只能是一種「假」的進度,至少在邏輯代碼加載完成以前,咱們都不能統計到進度,而邏輯代碼自身的進度也沒法統計。另外,咱們不可能監控到全部資源的加載狀況。github
因此頁面的加載進度都是「假」的,它存在的目的是爲了提升用戶體驗,使用戶不至於在打開頁面以後長時間面對一片空白,致使用戶流失。web
既然是「假」的,咱們就要作到「仿真」纔有用。仿真是有意義的,事實上用戶並不在意某一刻你是否是真的加載到了百分之幾,他只關心你還要load多久。因此接下來咱們就來實現一個頁面加載進度loading。ajax
首先準備一段loading的html:數組
<!DOCTYPE html>
<html>
<head>
<title>寫一個網頁進度loading</title>
</head>
<body>
<div class="loading" id="loading">
<div class="progress" id="progress">0%</div>
</div>
</body>
</html>
來點樣式裝扮一下:
.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%。這個作法是目前絕大部份進度條的作法。
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var 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 = 0
var timer = 0
progress(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函數,這個函數就是咱們主要的功能模塊,經過傳入一個目標值、一個時間間隔,就能夠模擬進度的演化過程。
目前來看,這個進度仍是有些問題的:
進度太平均,相同的時間間隔,相同的增量,不符合網絡環境的特色;
window.onload太快,咱們還來不及看清100%,loading就已經不見了;
每次第一階段都是在80%就暫停了,露餡兒了;
第一個點,咱們要讓時間間隔隨機,增量也隨機;第二個點很簡單,咱們延遲一下就行了;第三點也須要咱們隨機產生一個初始值。
增量隨機很好辦,如何讓時間間隔隨機?setInterval是沒法動態設置delay的,那麼咱們就要把它改造一下,使用setTimeout來實現。(setInterval跟setTimeout的用法和區別就不細說了吧?)
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var timer = 0
progress([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結束掉。
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var timer = 0
progress([80, 90], [1, 3], 100) // 使用數組來表示隨機數的區間
window.onload = () => {
progress(100, [1, 5], 10, () => {
window.setTimeout(() => { // 延遲了一秒再隱藏loading
$loading.hide()
}, 1000)
})
}
window.setTimeout(() => { // 設置5秒的超時時間
progress(100, [1, 5], 10, () => {
window.setTimeout(() => { // 延遲了一秒再隱藏loading
$loading.hide()
}, 1000)
})
}, 5000)
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
}
}
咱們直接設置了一個定時器,5s的時間來做爲超時時間。這樣作是能夠的。
but,仍是有問題,這個定時器是在js加載完畢以後纔開始生效的,也就是說,咱們忽略了js加載完畢以前的時間,這偏差可大可小,咱們設置的5s,實際用戶可能等待了8s,這是有問題的。咱們作用戶體驗,須要從實際狀況去考慮,因此這個開始時間還須要再提早一些,咱們在head裏來記錄這個開始時間,而後在js當中去作對比,若是時間差大於超時時間,那咱們就能夠直接執行最後的完成步驟,若是小於超時時間,則等待 剩餘的時間 事後,再完成進度。
先在head裏埋點,記錄用戶進入頁面的時間loadingStartTime:
<!DOCTYPE html>
<html>
<head>
<title>寫一個網頁進度loading</title>
<script>
window.loadingStartTime = new Date()
</script>
<script src="index.js"></script>
</head>
<body>
<div class="loading" id="loading">
<div class="progress" id="progress">0%</div>
</div>
</body>
</html>
而後,咱們對比 當前的時間 ,看是否超時:(爲了方便複用代碼,我把完成的部分封裝成函數complete)
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var timer = 0
var now = new Date() // 記錄當前時間
var timeout = 5000 // 超時時間
progress([80, 90], [1, 3], 100)
window.onload = () => {
complete()
}
if (now - loadingStartTime > timeout) { // 超時
complete()
} else {
window.setTimeout(() => { // 未超時,則等待剩餘時間
complete()
}, timeout - (now - loadingStartTime))
}
function complete () { // 封裝完成進度功能
progress(100, [1, 5], 10, () => {
window.setTimeout(() => {
$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) + '%')
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
}
}
至此,咱們算是完整地實現了這一功能。
然而,事情尚未結束,少年你太天真。
若是目的是爲了寫一個純粹障眼法的僞loading,那跟其餘loading的實現就沒什麼區別了,咱們作事講究腳踏實地,能實現的實現,不能實現的,爲了團隊和諧,咱們不得已坑蒙拐騙。那麼咱們還能更貼近實際狀況一點嗎?實際上是能夠的。
咱們來分析一個場景,假設咱們想讓咱們的loading更加真實一些,那麼咱們能夠選擇性地對頁面上幾個比較大的資源的加載進行跟蹤,而後拆分整個進度條,好比咱們頁面有三張大圖a、b、c,那麼咱們將進度條拆成五段,每加載完一張圖咱們就推動一個進度:
隨機初始化[10, 20] ->
圖a推動20%的進度 ->
圖b推動25%的進度 ->
圖c推動30%的進度 ->
完成100%
這三張圖要佔20% + 25% + 30% = 75%的進度。
問題是,若是圖片加載完成是按照順序來的,那咱們能夠很簡單地:10(假設初始進度是10%) -> 30 -> 55 -> 85 -> 100,但事實是,圖片不會按照順序來,誰早到誰晚到是說不許的,因此咱們須要更合理的方式去管理這些進度增量,使它們不會互相覆蓋。
咱們須要一個可以替咱們累計增量的變量next;
因爲咱們的progress都是傳目的進度的,咱們須要另一個函數add,來傳增量進度。
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var timer = 0
var now = new Date()
var timeout = 5000
var next = prg
add([30, 50], [1, 3], 100) // 第一階段
window.setTimeout(() => { // 模擬圖a加載完
add(20, [1, 3], 200)
}, 1000)
window.setTimeout(() => { // 模擬圖c加載完
add(30, [1, 3], 200)
}, 2000)
window.setTimeout(() => { // 模擬圖b加載完
add(25, [1, 3], 200)
}, 2500)
window.onload = () => {
complete()
}
if (now - loadingStartTime > timeout) {
complete()
} else {
window.setTimeout(() => {
complete()
}, timeout - (now - loadingStartTime))
}
function complete () {
add(100, [1, 5], 10, () => {
window.setTimeout(() => {
$loading.hide()
}, 1000)
})
}
function add (dist, speed, delay, callback) {
var _dist = random(dist)
if (next + _dist > 100) { // 對超出部分裁剪對齊
next = 100
} else {
next += _dist
}
progress(next, speed, delay, callback)
}
function progress (dist, speed, delay, callback) {
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) + '%')
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
}
}
咱們這裏爲了方便,用setTimeout來模擬圖片的加載,真實應用應該是使用image.onload。
以上,就是咱們一步步實現一個進度loading的過程了,演示代碼能夠戳個人codePen 寫一個網頁進度loading。
看似很簡單的一個功能,其實仔細推敲,仍是有不少細節要考慮的。
到這裏,其實真的已經完成了,代碼有點多有點亂是否是?你能夠整理一下,封裝成爲插件的。
然而,好吧,其實我已經把這個進度封裝成插件了。。。
沒錯,其實我就是來幫本身打廣告的。。。
好吧,github倉庫在此 ez-progress。
ez-progress 是一個web(僞)進度插件,使用 ez-progress 實現這個功能很是簡單:
var Progress = require('ez-progress')
var prg = new Progress()
var $loading = $('#loading')
var $progress = $('#progress')
prg.on('progress', function (res) {
var progress = parseInt(res.progress) // 注意進度取整,否則有可能會出現小數
$progress.html(progress + '%')
})
prg.go([60, 70], function (res) {
prg.complete(null, [0, 5], [0, 50]) // 飛通常地衝向終點
}, [0, 3], [0, 200])
window.onload = function () {
prg.complete(null, [0, 5], [0, 50]) // 飛通常地衝向終點
}
木油錯,94這麼簡單!
loading隨處可見,好比一個app常常會有下拉刷新,上拉加載的功能,在刷新和加載的過程當中爲了讓用戶感知到 load 的過程,咱們會使用一些過渡動畫來表達。最多見的好比「轉圈圈」,「省略號」等等。
網頁loading有不少用處,好比頁面的加載進度,數據的加載過程等等,數據的加載loading很好作,只須要在加載數據以前(before ajax)顯示loading效果,在數據返回以後(ajax completed)結束loading效果,就能夠了。
可是頁面的加載進度,須要一點技巧。
頁面加載進度一直以來都是一個常見而又晦澀的需求,常見是由於它在某些「重」網頁(特別是網頁遊戲)的應用特別重要;晦澀是由於web的特性,各類零散資源決定它很難是「真實」的進度,只能是一種「假」的進度,至少在邏輯代碼加載完成以前,咱們都不能統計到進度,而邏輯代碼自身的進度也沒法統計。另外,咱們不可能監控到全部資源的加載狀況。
因此頁面的加載進度都是「假」的,它存在的目的是爲了提升用戶體驗,使用戶不至於在打開頁面以後長時間面對一片空白,致使用戶流失。
既然是「假」的,咱們就要作到「仿真」纔有用。仿真是有意義的,事實上用戶並不在意某一刻你是否是真的加載到了百分之幾,他只關心你還要load多久。因此接下來咱們就來實現一個頁面加載進度loading。
首先準備一段loading的html:
<!DOCTYPE html>
<html>
<head>
<title>寫一個網頁進度loading</title>
</head>
<body>
<div class="loading" id="loading">
<div class="progress" id="progress">0%</div>
</div>
</body>
</html>
來點樣式裝扮一下:
.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%。這個作法是目前絕大部份進度條的作法。
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var 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 = 0
var timer = 0
progress(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函數,這個函數就是咱們主要的功能模塊,經過傳入一個目標值、一個時間間隔,就能夠模擬進度的演化過程。
目前來看,這個進度仍是有些問題的:
第一個點,咱們要讓時間間隔隨機,增量也隨機;第二個點很簡單,咱們延遲一下就行了;第三點也須要咱們隨機產生一個初始值。
增量隨機很好辦,如何讓時間間隔隨機?setInterval是沒法動態設置delay的,那麼咱們就要把它改造一下,使用setTimeout來實現。(setInterval跟setTimeout的用法和區別就不細說了吧?)
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var timer = 0
progress([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結束掉。
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var timer = 0
progress([80, 90], [1, 3], 100) // 使用數組來表示隨機數的區間
window.onload = () => {
progress(100, [1, 5], 10, () => {
window.setTimeout(() => { // 延遲了一秒再隱藏loading
$loading.hide()
}, 1000)
})
}
window.setTimeout(() => { // 設置5秒的超時時間
progress(100, [1, 5], 10, () => {
window.setTimeout(() => { // 延遲了一秒再隱藏loading
$loading.hide()
}, 1000)
})
}, 5000)
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
}
}
咱們直接設置了一個定時器,5s的時間來做爲超時時間。這樣作是能夠的。
but,仍是有問題,這個定時器是在js加載完畢以後纔開始生效的,也就是說,咱們忽略了js加載完畢以前的時間,這偏差可大可小,咱們設置的5s,實際用戶可能等待了8s,這是有問題的。咱們作用戶體驗,須要從實際狀況去考慮,因此這個開始時間還須要再提早一些,咱們在head裏來記錄這個開始時間,而後在js當中去作對比,若是時間差大於超時時間,那咱們就能夠直接執行最後的完成步驟,若是小於超時時間,則等待 剩餘的時間 事後,再完成進度。
先在head裏埋點,記錄用戶進入頁面的時間loadingStartTime
:
<!DOCTYPE html>
<html>
<head>
<title>寫一個網頁進度loading</title>
<script> window.loadingStartTime = new Date() </script>
<script src="index.js"></script>
</head>
<body>
<div class="loading" id="loading">
<div class="progress" id="progress">0%</div>
</div>
</body>
</html>
而後,咱們對比 當前的時間 ,看是否超時:(爲了方便複用代碼,我把完成的部分封裝成函數complete)
var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var timer = 0
var now = new Date() // 記錄當前時間
var timeout = 5000 // 超時時間
progress([80, 90], [1, 3], 100)
window.onload = () => {
complete()
}
if (now - loadingStartTime > timeout) { // 超時
complete()
} else {
window.setTimeout(() => { // 未超時,則等待剩餘時間
complete()
}, timeout - (now - loadingStartTime))
}
function complete () { // 封裝完成進度功能
progress(100, [1, 5], 10, () => {
window.setTimeout(() => {
$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) + '%')
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
}
}
至此,咱們算是完整地實現了這一功能。
然而,事情尚未結束,少年你太天真。
若是目的是爲了寫一個純粹障眼法的僞loading,那跟其餘loading的實現就沒什麼區別了,咱們作事講究腳踏實地,能實現的實現,不能實現的,爲了團隊和諧,咱們不得已坑蒙拐騙。那麼咱們還能更貼近實際狀況一點嗎?實際上是能夠的。
咱們來分析一個場景,假設咱們想讓咱們的loading更加真實一些,那麼咱們能夠選擇性地對頁面上幾個比較大的資源的加載進行跟蹤,而後拆分整個進度條,好比咱們頁面有三張大圖a、b、c,那麼咱們將進度條拆成五段,每加載完一張圖咱們就推動一個進度:
隨機初始化[10, 20] ->
圖a推動20%的進度 ->
圖b推動25%的進度 ->
圖c推動30%的進度 ->
完成100%
這三張圖要佔20% + 25% + 30% = 75%
的進度。
問題是,若是圖片加載完成是按照順序來的,那咱們能夠很簡單地:10(假設初始進度是10%) -> 30 -> 55 -> 85 -> 100,但事實是,圖片不會按照順序來,誰早到誰晚到是說不許的,因此咱們須要更合理的方式去管理這些進度增量,使它們不會互相覆蓋。
next
;progress
都是傳目的進度的,咱們須要另一個函數add
,來傳增量進度。var $loading = $('#loading')
var $progress = $('#progress')
var prg = 0
var timer = 0
var now = new Date()
var timeout = 5000
var next = prg
add([30, 50], [1, 3], 100) // 第一階段
window.setTimeout(() => { // 模擬圖a加載完
add(20, [1, 3], 200)
}, 1000)
window.setTimeout(() => { // 模擬圖c加載完
add(30, [1, 3], 200)
}, 2000)
window.setTimeout(() => { // 模擬圖b加載完
add(25, [1, 3], 200)
}, 2500)
window.onload = () => {
complete()
}
if (now - loadingStartTime > timeout) {
complete()
} else {
window.setTimeout(() => {
complete()
}, timeout - (now - loadingStartTime))
}
function complete () {
add(100, [1, 5], 10, () => {
window.setTimeout(() => {
$loading.hide()
}, 1000)
})
}
function add (dist, speed, delay, callback) {
var _dist = random(dist)
if (next + _dist > 100) { // 對超出部分裁剪對齊
next = 100
} else {
next += _dist
}
progress(next, speed, delay, callback)
}
function progress (dist, speed, delay, callback) {
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) + '%')
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
}
}
咱們這裏爲了方便,用setTimeout來模擬圖片的加載,真實應用應該是使用image.onload
。
以上,就是咱們一步步實現一個進度loading的過程了,演示代碼能夠戳個人codePen 寫一個網頁進度loading。
看似很簡單的一個功能,其實仔細推敲,仍是有不少細節要考慮的。
到這裏,其實真的已經完成了,代碼有點多有點亂是否是?你能夠整理一下,封裝成爲插件的。
然而,好吧,其實我已經把這個進度封裝成插件了。。。
沒錯,其實我就是來幫本身打廣告的。。。
好吧,github倉庫在此 ez-progress。
ez-progress 是一個web(僞)進度插件,使用 ez-progress 實現這個功能很是簡單:
var Progress = require('ez-progress')
var prg = new Progress()
var $loading = $('#loading')
var $progress = $('#progress')
prg.on('progress', function (res) {
var progress = parseInt(res.progress) // 注意進度取整,否則有可能會出現小數
$progress.html(progress + '%')
})
prg.go([60, 70], function (res) {
prg.complete(null, [0, 5], [0, 50]) // 飛通常地衝向終點
}, [0, 3], [0, 200])
window.onload = function () {
prg.complete(null, [0, 5], [0, 50]) // 飛通常地衝向終點
}
木油錯,94這麼簡單!
這多是我目前寫過最短的博文了,由此看出之前是有多麼的囉嗦,哈哈哈哈!