最近我在 CodePen 上看到了這樣一個有意思的動畫:
整個動畫效果是在一個標籤內,藉助了 SVG PATH 實現。其核心在於對漸變(Gradient)的究極利用。
完整的代碼你可以看看這裏 -- CodePen DEMO -- to the future 🍻 By Jane Ori]
源代碼還是非常非常複雜的,並且疊加了複雜的 SVG PATH 路徑。
我嘗試着將其稍微拆分成幾小塊,運用不同的 CSS 高階技巧從另外一個方面方向重新實現了一遍。因爲整個過程還是有非常多有意思的 CSS 技巧,本文就給大家分享一下。
實現上半部分背景加落日
首先,我們來實現上半部分的背景加落日效果:
大家可以先停頓思考下,這裏讓你來實現,會如何去做?需要多少個標籤?
好的,這裏,我們利用一個 DOM 標籤去完成這個圖形:
<div class="g-bg"><div>
背景色好做,使用一個徑向漸變或者線性漸變即可:
.g-bg {
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
}
如此,先實現一個背景:
好,接下來,我們使用其中一個僞元素實現落日的效果。
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
&::before {
content: "";
position: absolute;
bottom: 20%;
left: 10%;
right: 10%;
top: 10%;
background: radial-gradient(circle at 50% 100%, var(--color3), var(--color4) 55%, transparent 55.1%, transparent);
}
}
效果如下:
到這裏,我覺得算是出現了第一個技巧,也就是這一行代碼 background: radial-gradient(circle at 50% 100%, var(--color3), var(--color4) 55%, transparent 55.1%, transparent)
,它用於在一個矩形元素中,通過徑向漸變從實色到透明色,實現一個半圓。
技巧 1:可以利用徑向漸變,在一個矩形 DIV 元素中,通過徑向漸變從實色到透明色的變化,實現一個半圓。
我們繼續,接下來,切割這個圓形,得到這樣一種效果:
注意,這裏需要裁剪切割的地方不是白色,而是透明的,需要透出後面的背景色。
毫無疑問,這裏需要使用 mask 來完成,我們給僞元素加上 mask:
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
&::before {
content: "";
position: absolute;
bottom: 20%;
left: 10%;
right: 10%;
top: 10%;
background: radial-gradient(circle at 50% 100%, var(--color3), var(--color4) 55%, transparent 55.1%, transparent);
mask: linear-gradient(to top,
#000 0, #000 10%,
transparent 10%, transparent 13%,
#000 13%, #000 20%,
transparent 20%, transparent 22%,
#000 22%, #000 35%,
transparent 35%, transparent 36%,
#000 36%, #000 100%);
}
}
這樣,我們就實現了這個效果:
這裏,引出了第二個技巧:
技巧 2:利用 mask 可以對圖形進行裁剪,被裁剪區域將會變成透明。
好,接下來,我們需要在整個圖形上再疊加上豎形黑色條紋。這個其實也可以用 mask,如果整個圖形後面還有一層黑色背景。
當然,這裏我們也可以把另外一個僞元素利用起來,利用它,通過多重線性漸變(repeating-linear-gradient)實現這裏的豎形黑色條紋。
看看代碼:
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
&::before {
content: "";
// code of sun
}
&::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: repeating-linear-gradient(90deg, transparent 0, transparent 3px, rgba(0,0,0,.5) 4px, rgba(0,0,0,.5) 5px);
}
}
這裏,我們利用 repeating-linear-gradient
快速創建批量的豎形黑色條紋效果,得到這樣的效果:
這裏,得到技巧 3。
技巧 3:當你碰到大量重複有規律的線條,或者方塊圖形,你第一時間就應該想到在一個 DOM 中利用漸變而不是多個 DOM 去實現
好,至此,我們整個上半部分就實現了。
利用 -webkit-box-reflect 實現倒影
有了上面的基礎,接下來我們要得到完整的背景:
怎麼做呢?換個配色重新實現一遍嗎?當然不是,這裏我們利用 CSS 提供的倒影功能,可以快速完成這個操作。
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
-webkit-box-reflect: below;
&::before {
content: "";
// ...
}
&::after {
content: "";
// ...
}
}
我們給 .g-bg
加一個 -webkit-box-reflect: below
,意爲下方的倒影:
雖然是複製了一個一模一樣的 .g-bg
出來,但是和我們要的效果還相差很多啊,怎麼辦呢?
別急,-webkit-box-reflect: below
還提供,倒影偏移距離,倒影遮罩等屬性。
我們需要給下方的倒影,添加一個遮罩,修改一下 -webkit-box-reflect
的代碼:
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
-webkit-box-reflect: below -50px linear-gradient(rgba(255, 255, 255, .2), transparent);
&::before {
content: "";
// ...
}
&::after {
content: "";
// ...
}
}
這樣,我們就得到了這樣一種效果:
這裏,整個圖形其實是半透明的,我們在背後疊加上一層我們想要的色彩漸變,可以利用 body 的僞元素:
body {
&::before {
position: absolute;
content: "";
top: 50%;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(var(--c5), var(--c6));
}
}
.g-bg {
position: absolute;
background: radial-gradient(circle at 50% 100%, var(--color1), var(--color2));
-webkit-box-reflect: below -50px linear-gradient(rgba(255, 255, 255, .2), transparent);
&::before {
content: "";
// ...
}
&::after {
content: "";
// ...
}
}
倒影通過半透明和背後的漸變背景疊加,這樣,我們就完美實現了我們想要的整體背景效果:
這裏,我們可以引出技巧 4。
技巧 4:當出現重複的對稱圖形時,-webkit-box-reflect
也許能派上用場。
利用 CSS 3D 動畫實現線條動畫
好,主體背景完成了,下面,我們來試着實現 3D 線條動畫:
利用 CSS 3D,我們是可以實現這樣一種效果的。我們一步一步來拆解。
首先,我們需要實現這樣一種網格效果:
還記得上面的技巧 3 嗎?當你碰到大量重複有規律的線條,或者方塊圖形,你第一時間就應該想到在一個 DOM 中利用漸變而不是多個 DOM 去實現。
這種效果,其實利用漸變一個標籤就組足夠了:
<div class="grid"></div>
.grid {
background:
repeating-linear-gradient(var(--c1), var(--c1) 1px, transparent 1px, transparent 20px),
repeating-linear-gradient(90deg, var(--c1), var(--c1) 1px, transparent 1px, transparent 20px);
}
僅此而已,我們就能得到一個網格圖。
好的,接下來,需要利用 transform 讓他呈現一種 3D 視覺:
body {
perspective: 300px;
}
.grid {
position: absolute;
width: 300vw;
height: 600px;
left: -100vw;
top: 55vh;
transform-style: preserve-3d;
background:
repeating-linear-gradient(var(--c1), var(--c1) 1px, transparent 1px, transparent 20px),
repeating-linear-gradient(90deg, var(--c1), var(--c1) 1px, transparent 1px, transparent 20px);
transform: translate3d(0, 0, 0) rotateX(90deg);
transform-origin: 50% 0;
}
效果如下:
由於,整體繞 X 軸旋轉 90°,所以這裏的 top: 55vh
很重要。
由於旋轉圓心是 50% 0
,如果是 top: 50vh
, 相當於整個圖形會垂直於屏幕,如果 top
值小於 50vh,則整個網格是一種向上的翻轉效果:
接着,我們需要讓其運動起來。
我們嘗試添加一個 translateZ 的運動動畫:
.grid {
// ...
animation: move 10s infinite linear;
}
@keyframes move {
0% {
transform: translate3d(0, 0, -600px) rotateX(90deg);
}
100% {
transform: translate3d(0, 0, 600px) rotateX(90deg);
}
}
看看效果:
這裏有個很嚴重的問題,僅僅只是單個動畫,很難做到無限循環銜接。
因此,我們需要再加一組 Grid,動畫兩組動畫先後出發,來實現整個動畫的銜接。
<div class="grid"></div>
<div class="grid"></div>
.grid {
// ...
animation: move 10s infinite linear;
}
.grid:nth-child(2) {
animation: move 10s infinite -5s linear;
}
@keyframes move {
0% {
transform: translate3d(0, 0, -600px) rotateX(90deg);
}
100% {
transform: translate3d(0, 0, 600px) rotateX(90deg);
}
}
我們通過這麼一種方式:
- 兩組一模一樣的動畫,整個位移長度是 1200px,整個動畫持續 10s,緩動爲線性動畫
- 第一組出發 5s 後(剛好行進了 600px),第二組再出發,如此 infinite 反覆
- 整個 3D 動畫,在近屏幕端看上去就是無限循環的一種效果
- 這裏運用的是
-5s
,意思是提前 5s 出發,實際動畫效果也就不會有等待感
如下(這裏,爲了錄製 GIF,我整體是加快了動畫的速度):
可以看到,近屏幕端的動畫是連續不斷的,只是遠端會出現一定的閃爍。這裏,可以得到技巧 5。
技巧 5:利用 2 組動畫可以將一些有效在單組內的動畫無法實現的連續效果實現
這樣,疊加上上面的效果,我們就得到了這樣一種效果:
可以看到,很接近了。目前線條動畫遠處還有一些抖動。剛好,我們還差一個山峯的效果,可以把這塊瑕疵擋住。
使用 box-shadow 及 SVG 濾鏡實現山脈效果
OK,最後,我們在屏幕中間再疊加上一個山峯的效果就好。
這裏,原效果使用的是一長串導出的 SVG 路徑。如果我們沒有這種資源,只是想簡單模擬一下效果。這裏我給出一種可能可行的方案。
我首先利用一個圓角矩形進行旋轉,再配合容器的 overflow: hidden
得到一個小山峯:
<div class="g-mountain"></div>
.g-mountain {
position: absolute;
left: 0;
right: 0;
top: 15%;
bottom: 42%;
overflow: hidden;
&::after {
content: "";
position: absolute;
top: 78%;
background: #011d3f;
width: 15vw;
height: 15vw;
transform: rotate(-45deg);
}
}
大概是這樣一種效果:
好,如果我們想重複得到多個這樣的圖形,該怎麼辦呢?多個 DOM 嗎?不是的,這裏我們可以利用 box-shadow
複製自身。
.g-mountain {
// ...
&::after {
content: "";
position: absolute;
top: 78%;
background: #011d3f;
width: 15vw;
height: 15vw;
transform: rotate(-45deg);
box-shadow:
-3vw -3vw, 5vw 5vw,
10vw 10vw 0 3vw, 15vw 20vw 0 4vw,
22vw 22vw 0 6vw, 25vw 30vw 0 12vw,
38vw 36vw 0 1vw, 41vw 39vw 0 3vw,
45vw 45vw 0 2vw, 52vw 52vw 0 4vh,
55vw 55vw 0 1.5vw, 61vw 61vw 0 0.5vw, 68vw 68vw 0 0;
}
}
這樣,我們就用一個標籤,實現了一系列的“山”:
這裏,我們得到了技巧 6。
技巧 6:box-shadow
可以有效的複製自身,並且,可以利用第四個參數,擴散半徑,來等比例放大自身。
其實,到這裏,一個比較粗糙的還原就完成了。當然,有一點小問題是,山峯明顯不應該是一條條直線。能否營造出一種彎彎曲曲的外輪廓效果呢?
這個使用純 CSS 是比較難實現的,當然,好在這裏我們可以運用上之前給大家多次提及過的 SVG 濾鏡。
利用 feTurbulence
可以有效實現一些波形紋理效果。並且可以通過 CSS filter 快速引入。
<div class="g-mountain"></div>
<svg width="0">
<filter id="filter">
<feTurbulence id="turbulence" type="fractalNoise" baseFrequency=".03" numOctaves="20" />
<feDisplacementMap in="SourceGraphic" scale="30" />
</filter>
</svg>
.g-mountain {
// ...
filter: url('#filter');
&::after {
}
}
這裏,原本,整齊劃一的直線,立馬變得雜亂無章了起來,看起來更像是山脈的輪廓:
這裏,我們得出了技巧 7。
技巧 7:SVG 濾鏡可以通過 CSS 濾鏡快速引入,SVG 濾鏡可以實現一些 CSS 完成不了的事情,譬如一些特殊的紋理,波紋,煙霧顆粒感等等效果。
好,至此,我們就大體上按照自己的理解,重新實現了一遍上述的動畫,再做一些簡單的修飾,最終的效果如下:
CodePen Demo -- Pure CSS to the future
最後
今天的內容有點多,技巧也很猛。文中所有技巧在我過往的文章中都有非常高頻的出現次數,對其中細節不瞭解的可以在 iCSS 中通過關鍵字查找,好好補一補。
好了,本文到此結束,希望本文對你有所幫助 😃
更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。
如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。