我的小冊 《CSS 技術揭祕與實戰通關》上線了,想了解更多有趣、進階、系統化的 CSS 內容,可以猛擊 - LINK。
最近大家刷抖音,是否有刷到拉斯維加斯的新地標 「Sphere」:
場館內部的視覺效果非常驚人,其中一個效果讓我虎軀一震:
我的第一想法就是,這個看起來用 CSS 也可以實現嘛?還有 CSS 不能實現的?
本文,就將嘗試使用 CSS,大致還原這個效果。
其實,上述的動畫效果,本質就是一個 3D 立方體。
同時,3D 立方體上每個面存在顏色不一樣的文字,文字和顏色都在隨機變化。
也就是說,我們需要實現一個 3D 立方體:
同時,我們還需要實現這樣一個動畫效果 -- 文字和顏色都在隨機變化的平面效果:
兩者組合一下,再挪動 3D 元素的景深距離,就能實現我們想要的效果!
好,下面我們一個一個實現。
實現一個 3D 立方體,相對另外一個文字和顏色都在隨機變化的平面效果而言,屬於非常非常簡單的一步了。
我們在非常多篇文章中也講過具體的實現方式:
最常見的 3D 圖形,莫過於一個 3D 立方體。
如果沒有上下兩個面,只是一個 4 個面的圖形,大概是這樣:
這樣一個圖形,利用 CSS 3D,如何快速實現呢?
首先,構造這麼一個結構:
<div class="perspective">
<div class="container">
<div class="img">3</div>
<div class="img">D</div>
<div class="img">視</div>
<div class="img">圖</div>
</div>
</div>
4 個面,就是最內層的 4 個 .img
,首先,需要給兩個父容器,設定 3D 的屬性:
.perspective {
perspective: 3000px;
}
.container {
width: 400px;
height: 400px;
transform-style: preserve-3d;
}
簡單解釋一下:
perspective
可以作用於元素的後代,設定在最上層即可;transform-style: preserve-3d
設定給最終需要 3D 空間的元素的父容器之上,由於最終是 4 個 .img
需要 3D 空間,因此設定給 .container
即可。接下來,就是最為核心的,如何設定 4 個 .img
元素的 3D 變換,使之形成 3D 立方體。
技巧就是:先旋轉,再位移。
這裡給出一個俯視效果圖:
以上述 Demo 中的正方體為例子,class 為 .img
的 div 塊的高寬為 400px*400px
。那麼要利用 4 個 這樣的 div 拼接成一個正方體,需要分別將 4 個 div 繞 Y 軸旋轉 [90°, 180°, 270°, 360°],再 translateY(200px)
。
值得注意的是,一定是先旋轉角度,再偏移距離,這個順序很重要。
程式碼如下:
.img {
position: absolute;
top: 0;
left: 0;
width: 400px;
height: 400px;
}
@for $i from 1 through $imgCount {
.img:nth-child(#{$i}) {
transform: rotateY(($i * 90deg)) translateZ(200px);
}
}
效果如下:
此時,可能會覺得圖片太太太大了,此時,我們可以通過給中間層 .container
設定一個恰當的 translateZ
進行視覺大小上的調節。
.container {
transform: translateZ(-3000px);
}
這樣,就能得到恰當大小的立方體元素效果:
完整的程式碼,你可以戳這裡:CodePen Demo -- 3D Cube
當然,對於我們這個效果,我們 5 要五個面(前後左右與上方即可),因此,我們基於上述的基礎知識鋪墊,重新實現一個我們需要的框架結構:
<div class="perspective">
<div class="container">
<div class="g-panel"></div>
<div class="g-panel"></div>
<div class="g-panel"></div>
<div class="g-panel"></div>
<div class="g-panel"></div>
</div>
</div>
並且,我們希望我們的圖形是一個立方體,只需要稍微改造長寬和 translateZ()
的即可。這樣,我們就能得到一個前後左右與上方 5 個面的立方體元素。
示意效果如下:
OK,立方體我們先放在一邊。
接下來,我們嘗試來實現這個效果:
這個效果如果一個文字用一個 DIV 承載實現,那是非常容易的,但是這樣勢必會造成元素過多,再設定動畫效果,則會導致頁面太為卡頓。
所以,我們需要另闢蹊徑。這裡,我們可以使用多層漸變配合 background-clip: text
。
首先,我們利用等寬字型,隨機實現一列文字:
<div>ABCDEFGHIJKLMN</div>
div {
font-family: monospace;
text-align: center;
font-size: 25px;
width: 25px;
line-height: 25px;
color: #fff;
}
效果大致如下:
此時,如果我們再利用線性漸變,給每個字元的對應空間(也就 25px x 25px
),設定上不同的顏色,大概是這樣:
@function randomLinear($count) {
$value: '';
@for $i from 0 through ($count - 1) {
$value: $value + randomColor() + string.unquote(" 0 #{$i * 25}px,");
}
@return linear-gradient(string.unquote(#{$value}) randomColor() 0 100%);
}
@function randomColor() {
@return rgb(randomNum(255), randomNum(255), randomNum(255));
}
div {
// ...
background: randomLinear(14);
}
其中,randomLinear(14)
是一個 SASS 函數,引數 14 表示生成 14 層線性漸變,每一個文字區域的顏色都是隨機的,經過編譯後的其中一種結果如下:
div {
// ...
background: linear-gradient(#feea96 0 25px, #edde42 0 50px, #e2344a 0 75px, #cdab7e 0 100px, #e16c8b 0 125px, #dcdc7d 0 150px, #dcb42a 0 175px, #d6a587 0 200px, #984f71 0 225px, #221e34 0 250px, #5e9a69 0 275px, #a955e4 0 300px, #4e908f 0 325px, #8d177e 0 350px);
}
上面,我們按照每間隔 25px 的高度,利用線性漸變隨機設定了一種顏色,最終,能夠得到這麼個效果:
此時,我們只需要再設定 background-clip: text
,配合透明文字顏色 color: transparent
,就可以實現單個 div 內,單列文字,每個字型的顏色都是不一樣的:
div {
// ...
background: randomLinear(14);
background-clip: text;
color: transparent;
}
此時,效果如下:
當然,文字顏色可以隨機,那麼文字本身也應該隨機。這個不難,我們也可以藉助 SASS 函數,編寫一個隨機字元的函數,通過元素的偽元素 content 進行設定。
那麼此時,完整的程式碼可能是這樣的:
<div></div>
$str: 'QWERTYUIOPASDFGHJKLZXCVBNMabcdefghigklmnopqrstuvwxyz123456789';
$length: str-length($str);
@function randomLinear($count) {
$value: '';
@for $i from 0 through ($count - 1) {
$value: $value + randomColor() + string.unquote(" 0 #{$i * 25}px,");
}
@return linear-gradient(string.unquote(#{$value}) randomColor() 0 100%);
}
@function randomColor() {
@return rgb(randomNum(255), randomNum(255), randomNum(255));
}
@function randomChar() {
$r: random($length);
@return str-slice($str, $r, $r);
}
@function randomChars($number) {
$value: '';
@if $number > 0 {
@for $i from 1 through $number {
$value: $value + randomChar();
}
}
@return $value;
}
div {
position: relative;
width: 25px;
height: 350px;
&::before {
content: randomChars(14);
position: absolute;
font-family: monospace;
background: randomLinear(14);
background-clip: text;
color: transparent;
text-align: center;
font-size: 25px;
width: 25px;
line-height: 25px;
}
}
這樣,每次 div 內的文字,都是從上面 SASS 函數中 $str
變數中隨機取的:
接下來,我們需要實現文字的隨機跳變,也很好做,我們需要在一開始,隨機生成多個不同的 content,然後,藉助 CSS 動畫,進行切換。
div {
&::before {
content: randomChars(14);
--content1: "#{randomChars(14)}";
--content2: "#{randomChars(14)}";
--content3: "#{randomChars(14)}";
--content4: "#{randomChars(14)}";
animation: contentChange 1s infinite;
}
}
@keyframes contentChange {
20% {
content: var(--content1);
}
40% {
content: var(--content2);
}
60% {
content: var(--content3);
}
80% {
content: var(--content4);
}
}
這裡,我們一次生成了 5 個 content,其中 4 個用 CSS 變數儲存了起來,隨後,在 CSS 動畫中,利用提前生成好的 content,進行字元內容的替換,此時,整個效果如下:
隨機內容有了,單個字型顏色不一樣有了,就差顏色的隨機跳變動畫了,這個也非常好做,我們在多篇文章也提及過,利用 filter: hue-rotate()
可以快速實現內容的顏色切換。
div {
animation: colorChange 1s steps(12) infinite;
}
@keyframes colorChange {
100% {
filter: hue-rotate(360deg);
}
}
我們利用了 filter: hue-rotate()
加上了步驟動畫(steps),成功的實現了顏色的跳變!效果如下:
當然,我們最終要實現的是整個面隨機顏色加上隨機文字的跳變動畫,只需要在上述的基礎上,利用 SASS 函數,迴圈重複多列操作即可。基於上述所有內容的鋪墊,我們最終的單個面下的動畫效果程式碼如下:
<div class="g-container">
<div></div>
// ... 一個 32 個子 div
<div></div>
</div>
@use "sass:string";
$str: 'QWERTYUIOPASDFGHJKLZXCVBNMabcdefghigklmnopqrstuvwxyz123456789';
$length: str-length($str);
$size: 25;
$count: 41;
@function randomNum($max, $min: 0, $u: 1) {
@return ($min + random($max)) * $u;
}
@function randomLinear($count) {
$value: '';
@for $i from 0 through ($count - 1) {
$value: $value + randomColor() + string.unquote(" 0 #{$i * 25}px,");
}
@return linear-gradient(string.unquote(#{$value}) randomColor() 0 100%);
}
@function randomColor() {
@return rgb(randomNum(255), randomNum(255), randomNum(255));
}
@function randomChar() {
$r: random($length);
@return str-slice($str, $r, $r);
}
@function randomChars($number) {
$value: '';
@if $number > 0 {
@for $i from 1 through $number {
$value: $value + randomChar();
}
}
@return $value;
}
body,
html {
width: 100%;
height: 100%;
background: #000;
font-family: monospace;
}
.g-container {
position: relative;
width: 800px;
height: 800px;
display: flex;
animation: colorChange 1s steps(12) infinite;
div {
position: relative;
width: #{$size}px;
height: 800px;
flex-shrink: 0;
&::before {
position: absolute;
inset: 0;
text-align: center;
font-size: #{$size}px;
width: #{$size}px;
text-align: center;
line-height: #{$size}px;
color: transparent;
}
}
@for $i from 1 to $count {
div:nth-child(#{$i}) {
&::before {
content: randomChars(32);
--content1: "#{randomChars(32)}";
--content2: "#{randomChars(32)}";
--content3: "#{randomChars(32)}";
--content4: "#{randomChars(32)}";
animation: contentChange 1s infinite;
background: randomLinear(32);
background-clip: text;
}
}
}
}
@keyframes colorChange {
100% {
filter: hue-rotate(360deg);
}
}
@keyframes contentChange {
20% {
content: var(--content1);
}
40% {
content: var(--content2);
}
60% {
content: var(--content3);
}
80% {
content: var(--content4);
}
}
這樣,我們就成功的實現了單個平面下的,顏色隨機,文字隨機,且不斷變化的動畫效果:
單個平面下的完整程式碼,你可以戳這裡:CodePen Demo -- Single Panel Random Text
有了上面的立方體和單個平面的效果,要實現立體效果就不難了。我們嘗試將兩者結合起來。
改造原有的立方體結構,大致改成如下形式:
.perspective
.container
.g-panel
-for(var i=0; i<32; i++)
div
.g-panel
-for(var i=0; i<32; i++)
div
.g-panel
-for(var i=0; i<32; i++)
div
.g-panel
-for(var i=0; i<32; i++)
div
.g-panel
-for(var i=0; i<32; i++)
div
上面採用了 PUG 模板引擎來簡化程式碼,編譯後的效果如下:
<div class="perspective">
<div class="container">
<div class="g-panel">
<div></div>
// ... 32 個
<div></div>
<div class="g-panel">
<div></div>
// ... 32 個
<div></div>
<div class="g-panel">
<div></div>
// ... 32 個
<div></div>
<div class="g-panel">
<div></div>
// ... 32 個
<div></div>
<div class="g-panel">
<div></div>
// ... 32 個
<div></div>
</div>
</div>
這裡,我們只需要實現 5 個面的立方體即可(前後左右以及上方)。
每個 .g-panel
,實現一個我們上面鋪墊的單面文字跳變效果,這樣,我們就能得到這麼一個立體的 3D 立方體動畫效果:
接下來,我們只需要稍加偵錯,通過控制 perspective
和 transform: translateZ()
控制視覺上的縱深,將畫面的視角放置於整個立方體之中,即可得到這麼個效果:
好,最後,我們模擬文章開頭拉斯維加斯球的效果,讓頂部的平面,向下運動,實現一種天花板往下掉的動畫效果,最終,我們即可使用純 CSS,大致模擬出整個效果:
由於 GIF 錄製問題,實際效果會比 GIF 展示效果更為震撼。
使用 CSS 實現的完整的程式碼以及整個效果,你可以點選這裡進行檢視:CodePen Demo -- Las Vegas Sphere Cube Random Text
本文到此結束,希望對你有幫助