CSS 如何模擬“真實的”進度條?

XboxYan發表於2024-12-02

在頁面載入過程中,進度條的存在可以很好的緩解使用者的等待焦慮。

但是你可能不知道,90%以上的進度條只是模擬的,是假的進度條。沒辦法,大部分情況下都無法真實的算出資源的載入情況。既然無法避免,那我們就需要儘可能真實地模擬載入情況,給人一種頁面很輕快的感覺。

Kapture 2024-11-30 at 17.11.31

在這裡,我們可以僅使用 CSS 來模擬一個比較真實的進度條載入,一起看看吧

一、進度條載入和CSS緩衝動畫

進度條的實現很簡單,我們僅需要一個容器就行了,例如

<div class="progress"></div>

這裡有兩層結構,我們可以用一層偽元素來表示當前進度

.progress{
  position: relative;
  width: 300px;
  height: 10px;
  margin: 25px 0;
  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: progress 10s forwards;
}
@keyframes progress {
  to {
    width: 100%
  }
}

效果如下

Kapture 2024-11-30 at 15.44.18

這是預設的緩衝效果,也就是ease

image-20241130154705157

這是一種先快後慢的運動效果,其實也基本符合我們的需求,至少比勻速要好很多,例如下面是easelinear的對比,雖然總時長相同,但很明顯ease給人的感覺更快。

為了便於區分是否完成,我們在最後一個進度將進度條變為綠色

@keyframes progress {
  99%{
    background-color: #9747FF;
  }
  100%{
    background-color: #14AE5C;
    width: 100%;
  }
}

效果如下

Kapture 2024-11-30 at 15.51.33

不過,僅僅是ease還不夠,我們可以將起始速度調整的更快,這個可以透過cubic-bezier實現,比如這樣一個曲線

image-20241130155429149

前半段速度足夠快,後半段足夠慢,幾乎處於停止狀態,下面來對比一下這3者的效果

Kapture 2024-11-30 at 15.56.25

是不是感覺這個進度條更快了呢?

二、使用 linear 實現更精細的進度控制

前面的cubic-bezier雖然能實現先快後面的效果,但整體還是比較單調,畢竟沒什麼細節。

實際的載入過程可能會受到網路環境的影響,速度時快時慢,甚至會在中途停止一小會,這種有什麼辦法模擬呢?當然也是有的,這需要藉助全新的linear()函式

關於linear,之前在這篇文章中有詳細介紹:瞭解一下全新進化的CSS linear緩衝函式

簡單來說,就是我們可以透過linear設定足夠多的細節,來儘可能真實地模擬載入情況。

下面是我在Chrome控制檯上隨便加了十幾個關鍵點,有些點前後縱向距離比較接近,就表示這段時間內運動的距離很短,也就是速度很慢,可以模擬出卡頓的感覺,你也可以根據自己或者設計的感覺加入更多的關鍵點

image-20241130160747980

下面來看一下這個緩衝效果和前面的對比

Kapture 2024-11-30 at 16.11.50

是不是看著更像那麼一回事了,有種網路不穩定的感覺?

不過,這個linear的相容性還有點差,實際中可以做一下相容,不支援的仍然可以用前面的cubic-ezier方式,類似於這樣,畢竟是漸進增強而已

.progress {
  --ease: cubic-bezier(.08,.81,.29,.99);
}
@supports (animation-timing-function: linear(0, 1)) {
  .progress {
    --ease: linear(0 0%, 0.25 4.14%, 0.53 13.29%, 0.61 25.03%, 0.75 34.8%, 0.88 43.99%, 0.93 58.77%, 0.98 68.88%, 0.99 79.22%, 1 88.79%, 1 100%);
  }
}

三、主動完成進度條

大多數情況下,進度條只是一個過渡狀態,不會真正等到進度條走完,我們可能在監聽到某個關鍵資源載入成功或者某個功能初始化成功的實現就直接讓進度條走完(1s內),這該如何處理呢?

image-20241130164803232

直接替換肯定不行,那樣就不夠平滑了,動畫也不夠連貫。

這裡有個非常巧妙的方式,也非常簡單。

給進度條額外增加一個相同的動畫,就是時間不一樣,之前的是10s,新加的時長比較短,只有1s,預設情況下,只有前面的動畫是執行的,後面的動畫是暫停的,這裡用一個單獨的CSS變數來指定暫停狀態,實現如下

.progress::before{
  /*兩個相同的動畫,播放時長不同*/
  animation: progress 10s var(--ease) forwards, progress 1s var(--ease) forwards;
  animation-play-state: var(--running, running, paused);
}

然後在某個時刻,資源載入完成的時候,改變一下播放狀態,讓前面的暫停,後面的開始播放,後面的動畫時長只有1s,所以很快就能完成,這裡用點選事件來模擬載入完成

btn.onclick = function(){
  document.body.style.setProperty('--running', 'paused, running')
}

這樣在點選之後,可以在1s內平滑地完成整個進度,效果如下

Kapture 2024-11-30 at 16.35.11

還可以改變以下後面動畫的緩衝效果,也就是在“立刻完成”之後,全部都用預設的ease,而不是和前面保持一致

.progress::before{
  position: absolute;
  content: '';
  width: 0%;
  height: 100%;
  background: #9747FF;
  animation: progress var(--dur, 10s) var(--ease) forwards, progress 1s forwards;
}

這樣會不會更好一點呢?(“立即完成”後不應該有網路波動的感覺🤔)

Kapture 2024-11-30 at 16.56.04

以上完整程式碼可以檢視以下demo

  • CSS progress(codepen.io)')

四、總結一下

這樣一個非常實用的進度條模擬小技巧,你學到了嗎?下面總結一下

  1. 進度條可以使用者的等待焦慮
  2. 大多數情況下無法真實的算出資源的載入情況,所以需要儘可能真實的模擬進度載入情況
  3. CSS動畫預設的ease是先快後面,給人一種載入快的感覺
  4. 還可以透過cubic-bezier讓初始速度更快
  5. 透過linear可以給進度新增更多細節,以便於模擬更復雜的載入情況
  6. 實際頁面上進度條只是一個過渡狀態,不會真正等到進度條走完,需要主動觸發完成進度條
  7. 直接改成進度條狀態不夠平滑,動畫也不夠連貫
  8. 可以透過多動畫,設定不同的動畫時長,在真正需要完成的時候改變動畫暫停狀態,來實現主動完成進度載入

關注我,學習更多有趣的前端小技巧。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發 ❤❤❤

相關文章