CSS 如何模擬“真實”的進度條?
在頁面加載過程中,進度條的存在可以很好的緩解用戶的等待焦慮。
但是你可能不知道,90%以上的進度條只是模擬的,是假的進度條。沒辦法,大部分情況下都無法真實的算出資源的加載情況。既然無法避免,那我們就需要盡可能真實地模擬加載情況,給人一種頁面很輕快的感覺。
在這里,我們可以僅使用 CSS 來模擬一個比較真實的進度條加載,一起看看吧!
一、進度條加載和CSS緩沖動畫
進度條的實現很簡單,我們僅需要一個容器就行了,例如:
<divclass="progress"></div>
這里有兩層結構,我們可以用一層偽元素來表示當前進度。
.progress{position: relative;width:300px;height:10px;margin:25px0;border-radius:10px;overflow: hidden;background-color:#E4CCFF;}.progress::before{position: absolute;content:'';width:0%;height:100%;background:#9747FF;}
這樣我們就繪制了兩層,可以通過改變::before的寬度來改變當前進度,要實現進度從0% -> 100%非常簡單,只需要一個關鍵幀就行了。
.progress::before{animation: progress10s forwards;}@keyframes progress{to{width:100%}}
效果如下:
這是默認的緩沖效果,也就是ease。
這是一種先快后慢的運動效果,其實也基本符合我們的需求,至少比勻速要好很多,例如下面是ease和linear的對比,雖然總時長相同,但很明顯ease給人的感覺更快。
為了便于區分是否完成,我們在最后一個進度將進度條變為綠色。
@keyframes progress{99%{background-color:#9747FF;}100%{background-color:#14AE5C;width:100%;}}
效果如下:
不過,僅僅是ease還不夠,我們可以將起始速度調整的更快,這個可以通過cubic-bezier實現,比如這樣一個曲線。
前半段速度足夠快,后半段足夠慢,幾乎處于停止狀態,下面來對比一下這3者的效果
是不是感覺這個進度條更快了呢?
二、使用 linear 實現更精細的進度控制
前面的cubic-bezier雖然能實現先快后面的效果,但整體還是比較單調,畢竟沒什么細節。
實際的加載過程可能會受到網絡環境的影響,速度時快時慢,甚至會在中途停止一小會,這種有什么辦法模擬呢?當然也是有的,這需要借助全新的linear()函數。
簡單來說,就是我們可以通過linear設置足夠多的細節,來盡可能真實地模擬加載情況。
下面是我在Chrome控制臺上隨便加了十幾個關鍵點,有些點前后縱向距離比較接近,就表示這段時間內運動的距離很短,也就是速度很慢,可以模擬出卡頓的感覺,你也可以根據自己或者設計的感覺加入更多的關鍵點。
下面來看一下這個緩沖效果和前面的對比。
是不是看著更像那么一回事了,有種網絡不穩定的感覺?
不過,這個linear的兼容性還有點差,實際中可以做一下兼容,不支持的仍然可以用前面的cubic-ezier方式,類似于這樣,畢竟是漸進增強而已。
.progress{--ease:cubic-bezier(.08,.81,.29,.99);}@supports(animation-timing-function:linear(0,1)){.progress{--ease:linear(00%,0.254.14%,0.5313.29%,0.6125.03%,0.7534.8%,0.8843.99%,0.9358.77%,0.9868.88%,0.9979.22%,188.79%,1100%);}}
三、主動完成進度條
大多數情況下,進度條只是一個過渡狀態,不會真正等到進度條走完,我們可能在監聽到某個關鍵資源加載成功或者某個功能初始化成功的實現就直接讓進度條走完(1s內),這該如何處理呢?
直接替換肯定不行,那樣就不夠平滑了,動畫也不夠連貫。
這里有個非常巧妙的方式,也非常簡單。
給進度條額外增加一個相同的動畫,就是時間不一樣,之前的是10s,新加的時長比較短,只有1s,默認情況下,只有前面的動畫是執行的,后面的動畫是暫停的,這里用一個單獨的CSS變量來指定暫停狀態,實現如下:
.progress::before{/*兩個相同的動畫,播放時長不同*/animation: progress10svar(--ease) forwards, progress1svar(--ease) forwards;animation-play-state:var(--running, running, paused);}
然后在某個時刻,資源加載完成的時候,改變一下播放狀態,讓前面的暫停,后面的開始播放,后面的動畫時長只有1s
,所以很快就能完成,這里用點擊事件來模擬加載完成。
btn.onclick=function(){document.body.style.setProperty('--running','paused, running')}
這樣在點擊之后,可以在1s內平滑地完成整個進度,效果如下:
還可以改變以下后面動畫的緩沖效果,也就是在“立刻完成”之后,全部都用默認的ease,而不是和前面保持一致
.progress::before{position: absolute;content:'';width:0%;height:100%;background:#9747FF;animation: progressvar(--dur,10s)var(--ease) forwards, progress1s forwards;}
這樣會不會更好一點呢?(“立即完成”后不應該有網絡波動的感覺??)
以上完整代碼可以查看以下demo
- CSS progress(codepen.io)[1]
四、總結一下
這樣一個非常實用的進度條模擬小技巧,你學到了嗎?下面總結一下
- 進度條可以用戶的等待焦慮
- 大多數情況下無法真實的算出資源的加載情況,所以需要盡可能真實的模擬進度加載情況
CSS
動畫默認的ease
是先快后面,給人一種加載快的感覺- 還可以通過
cubic-bezier
讓初始速度更快 - 通過
linear
可以給進度添加更多細節,以便于模擬更復雜的加載情況 - 實際頁面上進度條只是一個過渡狀態,不會真正等到進度條走完,需要主動觸發完成進度條
- 直接改成進度條狀態不夠平滑,動畫也不夠連貫
- 可以通過多動畫,設置不同的動畫時長,在真正需要完成的時候改變動畫暫停狀態,來實現主動完成進度加載
[1]CSS progress(codepen.io): https://codepen.io/xboxyan/pen/emOmazz