【動畫進階】神奇的背景,生化危機4日食 Loading 動畫還原

2023-10-10 12:02:02

最近,在 Steam 玩一款老遊戲(生化危機 4 重置版),其中,每當遊戲轉場的過程中,都有這麼一個有趣的 Loading 動畫:

整個效果有點類似於日食效果,中間一圈黑色,向外散發著太陽般的光芒。

本文,我們將嘗試使用 CSS,還原這個效果。

整個效果做出來,類似於如下兩個動畫效果這樣:

實現主體效果

其實,整個效果,去掉中間黑色的遮罩,是這個樣子的:

所以,我們的目標就變成了,如何使用 CSS,實現上述這個圖形效果。

角向漸變

到這裡,思考一圈 CSS 中的各種屬性,和這個圖形能掛上鉤的,幾乎就只有角向漸變 conic-gradient 了。

我們可以利用多重角向漸變,試著畫一個類似的圖形 -- 從單個顏色到透明,再多次迴圈鋪滿 360° 的整個圖形

<div></div>
body {
    background: #000;
}
div {
    width: 200vw;
    height: 200vh;
    background: 
        repeating-conic-gradient(
            rgba(0, 136, 204, 0.77), 
            rgba(150, 157, 100, 0.72) 2%, 
            rgba(230, 247, 200, 0.82) 3%, 
            transparent 4%,
            transparent 5%
    );
}

我們隨機設定了 conic-gradient() 中的顏色 A顏色 B顏色 C到透明的變化,可以得到這麼一張圖形:

注意,對於上面的顏色沒有任何要求,隨機設定都可以。

我們可以讓這個圖形旋轉起來,簡單加上一個旋轉動畫:

div {
    animation: rotate 2s ease-in-out infinite;
}
@keyframes rotate { 
    to { 
        transform: rotate(1turn); 
    } 
}

效果如下:

我們仔細觀察一下,我們要的最終效果,其實要求邊緣是毛刺狀,而不是連續的影象:

這一步要怎麼實現呢?其實也非常簡單,我們只需要在原影象上,疊加一層從影象主色到黑色的徑向漸變即可

我們可以藉助偽元素實現這個疊加遮罩:

div::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(rgba(150, 157, 100, 0.32), rgba(0, 0, 0, 1) 45vmin, rgba(0, 0, 0, 1));
}

這裡我們實現了這麼一個漸變:

  • radial-gradient(rgba(150, 157, 100, 0.32), rgba(0, 0, 0, 1) 45vmin, rgba(0, 0, 0, 1)):其核心就是實現了從某一個實體顏色(選取一個上面角向漸變圖形用到的主要顏色)到黑色的一個徑向漸變效果

這樣,我們就將邊緣改造的不那麼突兀了!

效果如下:

設定多層背景

基於上述的原理及技巧,我們重新構思一下整個動畫,上面只有一層角向背景的背景在運動。

那麼,如果我們設定多層背景,並且,設定他們正向、反向一起運動呢?

我們來實現一個 4 層角向漸變背景的動畫效果,並且使用最終我們想要的黃色為主題色,:

<div class="g-container">
    <div class="g-circle g-circle1"></div>
    <div class="g-circle g-circle2"></div>
    <div class="g-circle g-circle3"></div>
    <div class="g-circle g-circle4"></div>
</div>

完整的 CSS 程式碼如下:

body{
    width: 100%;
    height: 100%;
    background: #000;
}

.g-container {
    position: absolute;
    width: 80vmax;
    height: 80vmax;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

    .g-circle {
        position: absolute;
        inset: 0;
        border-radius: 50%;
    }
    
    .g-circle1 {
        background: 
            repeating-conic-gradient(
                from 0deg at 50% 50%,
                transparent 0%,
                rgba(255, 230, 8, 0.69) 1%,
                transparent 6%
        );
        animation: 13s linear rotate infinite reverse;
    }
    
    .g-circle2 {
        background: 
            repeating-conic-gradient(
                from 19deg at 50% 50%,
                transparent 0%,
                rgba(250, 240, 20, 0.78) 1.2%,
                rgba(250, 240, 20, 0.78),
                transparent 4.8%,
                transparent 7.6%
        );
        animation: 9s linear -2s rotate infinite;
    }
    
    .g-circle3 {
        background: 
            repeating-conic-gradient(
                from 37deg at 50% 50%,
                transparent 0%,
                rgba(250, 240, 20, 0.78) 4%,
                transparent 7.9%,
                transparent 12%
        );
        animation: 17s linear rotate infinite reverse;
    }
    
    .g-circle4 {
        background: 
            repeating-conic-gradient(
                from 103deg at 50% 50%,
                transparent 0%,
                rgba(250, 240, 20, 0.5) 5%,
                rgba(250, 240, 20, 0.27) 7%,
                transparent 12%
        );
        animation: 7s linear rotate infinite;
    }
}

@keyframes rotate {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(359deg);
    }
}

上面的程式碼,做了幾件核心事情:

  1. 4 層背景重疊在一起
  2. 4 層背景設定不同的重複角向漸變圖案 repeating-conic-gradient()
  3. 4 層背景兩個順時針旋轉、兩個逆時針旋轉
  4. 動畫的引數各不相同

這樣,我們能得到這麼一個效果:

此時,我們再在上述圖形的基礎上,疊加上一層遮罩 mask,將圖案的邊緣黑化:

<div class="g-container">
    <div class="g-circle g-circle1"></div>
    <div class="g-circle g-circle2"></div>
    <div class="g-circle g-circle3"></div>
    <div class="g-circle g-circle4"></div>
 +  <div class="g-circle g-mask"></div>
</div>
.g-container .g-mask {
    position: absolute;
    inset: -200px;
    background: 
        radial-gradient(
            rgba(250, 240, 20, 0.2) 0,
            rgba(0, 0, 0, .8) calc(40vmax - 15vmax),
            #000 calc(40vmax - 5vmax),
            #000 100%
        );
}

這裡是一個比原容器稍微大的新容器(注意 inset: 200px),再設定從中心想外的徑向漸變,最外層顏色為黑色。

這樣,我們就能得到我們想要的效果了:

到這裡,完整的程式碼,你可以戳這裡:CodePen Demo -- Conic-gradient Pic

使用混合模式

當然,上述其實只是一種實現該圖形動畫的方式。

我們還可以藉助混合模式,得到類似的效果。

這一次,我們將藉助 SASS 的隨機函數,隨機生成不同的角向漸變背景,大致的程式碼如下:

<div></div>
@function randomNum($max, $min: 0, $u: 1) {
	@return ($min + random($max)) * $u;
}

@function randomConicGradient() {
	$n: 16 + random(16);
	$list: ();
	
	@for $i from 0 to $n {
		$list: $list, rgba(hsl(100, randomNum(250, 5, 10%), randomNum(1, 1, 1%)), randomNum(100, 0, .01));
	}
		
	@return conic-gradient($list, nth($list, 1));
}

body {
    overflow: hidden;
}

div {
    width: 100vw;
    height: 100vh;
	margin: 0;
	background: 
		radial-gradient(hsl(9, randomNum(100, 75, 1%), randomNum(100, 75%, 1%)), black);
	
	&:before, &:after {
		position: absolute;
		top: 50%; left: 50%;
		margin: -100vmax; 
		width: 200vmax; 
                height: 200vmax;
		opacity: .5;
		animation: rotate randomNum(100, 25, .1s) ease-in-out infinite;
		content: '';
	}
	
	&:before { background: randomConicGradient(); }
	&:after {
		background: randomConicGradient();
		animation-duration: randomNum(100, 25, .1s);
		animation-direction: reverse;
	}
}

@keyframes rotate { 
    to { 
        transform: rotate(1turn); 
    } 
}

上述的程式碼核心做了 3 件事:

  1. 元素本身設定了一個簡單的從隨機顏色到黑色的徑向漸變
  2. 元素的兩個偽元素藉助 SASS 隨機生成不同的角向漸變背景
  3. 兩個偽元素做反向的旋轉動畫

這樣,我們可以得到這麼一種隨機效果:

這裡可能大家光看程式碼還是會有些費勁,我給大家再拆解一下,上面的圖形,大致是由下述方式疊加而來(由於顏色都是隨機生成的,所以更多的看每個結構實現了什麼樣的圖形):

嘿,有點意思,不過別急,此時,我們再給兩個偽元素,新增上一個混合模式 mix-blend-mode: overlay

div {
    // ...
	
    &:before, &:after {
        mix-blend-mode: overlay;
    }
}

這樣,整個效果就變成了:

由於,顏色是隨機的,重新整理頁面或者簡單改變一些顏色引數,得到的效果就會很不一樣,也有可能是這樣的:

或者這樣的:

藉助混合模式,我們實現了更為酷炫的效果,上述的 DEMO,完整的程式碼在這裡:CodePen Demo -- Animation conic-gradient

還原題圖效果

好,到這裡,我們再回歸最開始我們希望實現的效果:

有了上面的鋪墊,再看這個效果就沒那麼複雜了,本質就是在我們上述實現的圖形中間,鏤空一個黑色區域。

首先,我們藉助上述鋪墊的內容,實現這麼一個圖形:

接著,我們在內部,通過 mask,進行一個徑向漸變的鏤空即可,加上這麼一句簡單的程式碼:

div {
    mask: radial-gradient(transparent, transparent 55%, #000 56%, #000);
}

需要結合實現 background 的引數進行偵錯。

這樣,我們就成功的實現一個類似的 Loading 圖形:

完整的程式碼,你可以戳這裡:CodePen Demo -- Animation conic-gradient & Mask

我們再看一個由 CodePen 上由 Yoav Kadosh 實現的另外一個原理類似的有意思的效果:

經由本文介紹的技巧,我們還可以演化出許多有意思的效果,讀者朋友可以自行探索!

當然,我們不難看出,CSS 還是非常有意思的。

最後

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