【動畫進階】有意思的網格下落漸次載入效果

2023-07-18 12:01:15

最近,群友貼了一個非常有意思的動畫效果,整體動畫效果如下:

點選某個按鈕後,觸發一個動畫效果,原本的網格內容,將按順序(又帶點隨機的效果)從高處下落進行載入填充動畫。

當然,我個人認為這個動畫有點華而不實,主要體現在這個動畫一次需要耗費較長時間,有點消磨人的耐心。不過確實一個很好的 CSS 動畫教學案例。

而本文,就將具體去剖析這個動畫,如果使用純 CSS,應該如何實現。

還原佈局結構

首先,我們需要大致還原佈局結構,這個並非整個動畫的核心。

整個動畫的核心在於元素按順序(又帶點隨機的效果)從高處下落漸次進行下落載入填充動畫

當然,還原這個佈局也非常簡單,這裡我們藉助 flex 佈局快速實現,幾個關鍵點:

  1. 每個 flex-item 寬度不固定:這一點可以使用 SASS 函數進行模擬,隨機生成不同寬度的 flex-item
  2. 每個 flex-item 背景色隨機:這一點同樣也可以藉助 SASS 函數 實現
  3. 整體水平居中:這個也簡單,父容器新增 justify-content: center; 即可

這樣,整個佈局的大致程式碼如下:

<ul>
  <li></li>
  <li></li>
   // ... 假設一共 50 個 li
  <li></li>
</ul>
$count: 51;
@function randomNum($max, $min: 0, $u: 1) {
    @return ($min + random($max)) * $u;
}
@function randomColor() {
    @return rgb(randomNum(255), randomNum(255), randomNum(255));
}

ul {
    display: flex;
    gap: 4px;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    width: 640px;
    height: 420px;
}
li {
    flex-shrink: 0;
    height: 30px;
    border-radius: 30px;
}

@for $i from 1 to $count {
    li:nth-child(#{$i}) {
        width: #{randomNum(110, 90)}px;
        background: randomColor();
    }
}

簡單解釋一下:

  1. 利用了 SASS 的迴圈函數簡化程式碼量
  2. 實現了兩個 SASS 函數,利用這兩個函數隨機生成不同寬度不同顏色的 li
  • randomNum(): 用於生成範圍內的亂數
  • randomColor(): 用於生成隨機顏色值

這樣,我們就可以快速得到這樣一個佈局效果:

下落動畫

接下來,我們來實現元素的下落動畫。

首先,我們撇開多元素的按順序又帶點隨機的延遲下落,值聚焦於單個 item 的下落動畫,它其實是這麼個動畫效果:

整個動畫的核心步驟大致是:

  1. 預設是狀態下,元素是在最終的 flex 佈局狀態下,且是可見狀態
  2. 動畫開始時,元素將消失,然後從上方,在一個縮小狀態下,有一個小幅度向上運動
  3. 接著元素向下運動到目標位置(終止狀態)
  4. 元素從縮小狀態,放大為正常狀態
  5. 並且,動畫結束後,需要維持在最後一幀,因此需要使用(animation-fill-mode: forwards

對 CSS 動畫細節和各個屬性還不太瞭解的,建議你看看我的這本小冊 -- CSS 技術揭祕與實戰通關

完成上面的拆解後,單個動畫實現起來就非常輕鬆了:

li {
    opacity: 0;

    &hover {
            animation: falldown 1s forwards;
    }
}

@keyframes falldown {
    0% {
        transform: translateY(-180px) scaleX(.1) scaleY(.3);
        opacity: 1;
    }
    20% {
        transform: translateY(-200px) scaleX(.6) scaleY(.3);
    }
    75% {
        transform: translateY(0) scaleX(.6) scaleY(.3);
    }
    100% {
        transform: translateY(0)  scaleX(1) scaleY(1);
        opacity: 1;
    }
}

這樣,我們就輕鬆的實現了單個的動畫效果:

如果 50 個 item 同時作用這個動畫,就是這樣的效果:

實現延遲隨機下落

當然,到這裡,整個動畫都還是平平無奇的

我們需要最後的點睛之筆,實現按順序又帶點隨機的延遲下落效果,以及讓整體的動畫更加自然。

這裡的核心就在於:

  1. 利用 animation-delay 製造延遲效果,但是不能是順序延遲,需要帶點隨機效果
  2. 利用 animation-timing-function 緩動函數,控制動畫的速度曲線,使動畫變化更為平滑
  3. 控制縮短每一個 item 的動畫時長

方法明確了,剩下的就是偵錯動畫效果了,最終,經過一番偵錯,完整的動畫效果的程式碼就完成啦。

程式碼量真的不多:

$count: 51;
@function randomNum($max, $min: 0, $u: 1) {
    @return ($min + random($max)) * $u;
}
@function randomColor() {
    @return rgb(randomNum(255), randomNum(255), randomNum(255));
}

ul {
    display: flex;
    gap: 4px;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    width: 660px;
    height: 420px;
}
li {
    flex-shrink: 0;
    height: 30px;
    border-radius: 30px;
}

@for $i from 1 to $count {
    li:nth-child(#{$i}) {
        width: #{randomNum(110, 90)}px;
        background: randomColor();
    }
}

ul:hover {
    li {
        opacity: 0;
    }
    @for $i from 1 to $count {
        li:nth-child(#{$i}) {
            animation: falldown .3s cubic-bezier(.44,.02,.65,1.3) #{50 * ($count - $i) + (random(150) - random(300))}ms forwards;
        }
    }
}
@keyframes falldown {
    0% {
        transform: translateY(-180px) scaleX(.1) scaleY(.3);
        opacity: 1;
    }
    20% {
        transform: translateY(-200px) scaleX(.6) scaleY(.3);
    }
    75% {
        transform: translateY(0) scaleX(.6) scaleY(.3);
    }
    100% {
        transform: translateY(0)  scaleX(1) scaleY(1);
        opacity: 1;
    }
}

核心,就在於這一句,animation: falldown .3s cubic-bezier(.44,.02,.65,1.3) #{50 * ($count - $i) + (random(150) - random(300))}ms forwards,需要好好的消化理解。

這樣,我們就基本完美的復刻了原效果:

Gif 錄製存在掉幀行為,原效果會比 GIF 錄製出來的更好。完整的程式碼你可以戳這裡:

CodePen Demo -- Flex Item Fall Down

總結一下

好了,本文到此結束,希望對你有幫助