我的小冊 《CSS 技術揭秘與實戰通關》上線了,想了解更多有趣、進階、系統化的 CSS 內容,可以猛擊 - LINK。
在 CSS 還原拉斯維加斯球數字動畫 - 掘金 一文中,我們利用純 CSS,實現了一個非常 Amazing 的動畫效果:
其中一個核心點就是,我們利用瞭如下的程式碼,在一個 DIV 平面內,實現了單個平面下的隨機文字隨機顏色效果。
效果如下:
其中的 HTML 程式碼大致如下:
<div class="g-container">
<div></div>
// ... 一個 32 個子 div
<div></div>
</div>
這裡為了實現上述效果,其實是用了 32 列,每列是一個 DIV。
emmm,對於追求極致的我們,32 個 DIV 確實不太優雅了。那麼,CSS 有沒有什麼方式,能夠單個標籤實現多列多格子,每個格子顏色不一致呢?像是這樣:
答案當然是可以。本文,我們就將一起來探尋,使用 CSS 如何實現單標籤下多色塊,及單標籤下隨機文字隨機顏色動畫效果。
多重背景的威力
思考一下,單個 DIV,我們如何能夠實現下述效果呢,譬如一個 DIV 內,有 36 種不同的顏色:
這裡的核心,其實就是需要藉助多重背景。
正常而言,我們的 DIV 只能有一個 background,設定一種顏色,像是這樣:
<div></div>
div {
width: 300px;
height: 300px;
background: #000;
}
效果如下:
但是,合理利用漸變語法的規則,利用多重漸變,我們就可以實現多重背景,我們改造一些上述程式碼:
div {
position: relative;
margin: auto;
width: 300px;
height: 300px;
background-image:
linear-gradient(90deg, #000, #000),
linear-gradient(90deg, #f00, #f00);
background-size: 50% 100%, 50% 100%;
background-position: 0 0, 150px 0;
background-repeat: no-repeat;
}
利用多重背景的能力,我們就得到了黑色和紅色兩個色塊:
我們還可以繼續拆分,1 拆 4:
div {
position: relative;
margin: auto;
width: 300px;
height: 300px;
background-image:
linear-gradient(90deg, #000, #000),
linear-gradient(90deg, #0f0, #0f0),
linear-gradient(90deg, #00f, #00f),
linear-gradient(90deg, #f00, #f00);
background-size: 50% 50%, 50% 50%, 50% 50%, 50% 50%;
background-position: 0 0, 150px 0, 0 150px, 150px 150px;
background-repeat: no-repeat;
}
效果如下:
它其實是這麼個意思,看下面這張圖就能很好的理解:
這裡我們只標識出了黑色色塊和紅色色塊,另外兩個色塊的原理也是一樣的。
理解了這一點之後,我們要實現如下這個圖形就非常輕鬆了:
當然,這裡有個問題,我們手動去寫那麼多重漸變的程式碼,工作量是非常之大的,因此,我們可以嘗試封裝一個 SCSS 函式或者 mixin 幫助我們減輕程式碼量。
@use "sass:string";
@function randomNum($max, $min: 0, $u: 1) {
@return ($min + random($max)) * $u;
}
@mixin randomLinear($rows: 6, $cols: 8) {
$bg: null;
$pos: null;
$px: 100% / ($cols - 1);
$py: 100% / ($rows - 1);
@for $i from 0 through $rows - 1 {
@for $j from 0 through $cols - 1 {
@if ($bg) {
$bg: $bg + string.unquote(",");
$pos: $pos + string.unquote(",");
}
$color: randomColor();
$bg: $bg + linear-gradient(to right, $color, $color);
$pos: $pos + string.unquote("#{$j * $px} #{$i * $py}");
}
}
background: {
image: $bg;
size: (100% / $cols) (100% / $rows);
repeat: no-repeat;
position: $pos;
}
}
@function randomColor() {
@return rgb(randomNum(205, 50), randomNum(255), randomNum(255));
}
div {
@include randomLinear(6, 6);
}
這裡,我們藉助 SCSS 封裝了一個 randomLinear
的 mixin,它接收兩個引數,分別表示行數和列數,基於上面的 background 拆分,實現了多重漸變,如此一來,我們就能在單個 DIV 下得到這樣一個隨機的多色塊格子圖:
審查元素,SCSS 編譯後的 CSS 程式碼其實就是這樣的:
好,在此基礎上要實現顏色的隨機變化也非常簡單,我們只需要配合 filter: hue-rotate()
變換即可。
程式碼如下:
div {
@include randomLinear(6, 6);
animation: colorChange 1s infinite steps(10);
}
@keyframes colorChange {
100% {
filter: hue-rotate(360deg);
}
}
這裡巧妙的利用了 steps(10)
,讓整個圖形在 1s 內進行 10 次 hue-rotate()
變化。
這裡的核心點有兩個:
- 利用
filter: hue-rotate(360deg)
濾鏡,能夠實現顏色的切換 - 利用
steps(10)
實現了逐幀動畫而不是連續的補間動畫
如此一來,我們就能得到如下效果,實現了單個標籤內多個不同色塊,並且可以實現動畫變換:
結合 background-clip: text 實現文字效果
接下來,我們需要實現單個標籤下的隨機文字、隨機顏色的動畫效果。也就是下圖右邊的效果:
有了上面的鋪墊,其實整個效果就剩下兩步:
- 利用
background-clip: text
實現從色塊到文字的裁剪變化 - 藉助 SCSS 函式及 CSS 變數,實現隨機文字的變化
首先,與 CSS 還原拉斯維加斯球數字動畫 - 掘金 一文中一樣,藉助 SCSS 函式,編寫一個隨機字元的函式,透過元素的偽元素 content 進行設定,並且,我們把背景色,也設定給元素的偽元素:
$str: 'QWERTYUIOPASDFGHJKLZXCVBNMabcdefghigklmnopqrstuvwxyz123456789';
$length: str-length($str);
@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 {
width: 300px;
height: 300px;
font-size: 50px;
line-height: 50px;
letter-spacing: 25px;
word-wrap: break-word;
font-family: monospace;
&::before {
content: randomChars(36);
position: absolute;
inset: 0;
@include randomLinear(6, 6);
color: transparent;
}
}
這裡,有幾個細節點再簡單講解一下:
- 為了讓每個字元對齊,我們使用了
font-family: monospace
等寬字元,並且利用font-size
和letter-spacing
確保一行只能放下 6 個字元 - 利用
randomChars
SCSS 函式,隨機從我們定義的$str
SCSS 字串變數中取 36 個隨機字元 @include randomLinear(6, 6)
就是上面鋪墊的隨機漸變背景
如此一來,我們就能得到這麼一個效果:
此時,我們只需要再給元素的偽元素設定一個 background-clip: text
配合文字顏色 transparent
,即可得到色塊裁剪到只剩下文字部分的效果:
div {
// ...
&::before {
//...
color: transparent;
background-clip: text;
}
}
效果如下:
好,那如何再讓整個文字隨機變換起來呢?我們只需提前生成多個不同的隨機文字 content
,然後進行動畫切換即可,像是這樣:
div {
// ...
&::before {
content: randomChars(36);
--content1: "#{randomChars(36)}";
--content2: "#{randomChars(36)}";
--content3: "#{randomChars(36)}";
--content4: "#{randomChars(36)}";
--content5: "#{randomChars(36)}";
--content6: "#{randomChars(36)}";
--content7: "#{randomChars(36)}";
--content8: "#{randomChars(36)}";
--content9: "#{randomChars(36)}";
color: transparent;
background-clip: text;
animation: contentChange 1.5s infinite linear;
}
}
@keyframes contentChange {
10% {
content: var(--content1);
}
20% {
content: var(--content2);
}
30% {
content: var(--content3);
}
40% {
content: var(--content4);
}
50% {
content: var(--content5);
}
60% {
content: var(--content6);
}
70% {
content: var(--content7);
}
80% {
content: var(--content8);
}
90% {
content: var(--content9);
}
}
這樣,文字也能隨機動起來了(當然,此處其實是偽隨機):
最後,把上面的 hue-rotate
動畫重新開啟,就能讓文字顏色也隨機變換!
至此,完整的程式碼如下:
<div></div>
@use "sass:string";
@import url('https://fonts.googleapis.com/css2?family=Righteous&family=Ubuntu+Mono&display=swap');
$str: 'QWERTYUIOPASDFGHJKLZXCVBNMabcdefghigklmnopqrstuvwxyz123456789';
$length: str-length($str);
@function randomNum($max, $min: 0, $u: 1) {
@return ($min + random($max)) * $u;
}
@mixin randomLinear($rows: 6, $cols: 8) {
$bg: null;
$pos: null;
$px: 100% / ($cols - 1);
$py: 100% / ($rows - 1);
@for $i from 0 through $rows - 1 {
@for $j from 0 through $cols - 1 {
@if ($bg) {
$bg: $bg + string.unquote(",");
$pos: $pos + string.unquote(",");
}
$color: randomColor();
$bg: $bg + linear-gradient(to right, $color, $color);
$pos: $pos + string.unquote("#{$j * $px} #{$i * $py}");
}
}
background: {
image: $bg;
size: (100% / $cols) (100% / $rows);
repeat: no-repeat;
position: $pos;
}
}
@function randomColor() {
@return rgb(randomNum(205, 50), 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 {
width: 300px;
height: 300px;
color: #fff;
font-size: 50px;
line-height: 50px;
letter-spacing: 25px;
word-wrap: break-word;
animation: colorChange 1.5s infinite steps(10);
&::before {
--content1: "#{randomChars(36)}";
--content2: "#{randomChars(36)}";
--content3: "#{randomChars(36)}";
--content4: "#{randomChars(36)}";
--content5: "#{randomChars(36)}";
--content6: "#{randomChars(36)}";
--content7: "#{randomChars(36)}";
--content8: "#{randomChars(36)}";
--content9: "#{randomChars(36)}";
content: randomChars(36);
position: absolute;
inset: 0;
color: transparent;
background-clip: text;
animation: contentChange 1.5s infinite linear;
}
}
@keyframes colorChange {
100% {
filter: hue-rotate(340deg);
}
}
@keyframes contentChange {
10% {
content: var(--content1);
}
20% {
content: var(--content2);
}
30% {
content: var(--content3);
}
40% {
content: var(--content4);
}
50% {
content: var(--content5);
}
60% {
content: var(--content6);
}
70% {
content: var(--content7);
}
80% {
content: var(--content8);
}
90% {
content: var(--content9);
}
}
效果如下:
完整的程式碼,你可以戳這裡:CodePen Demo -- Single Div Random Text And Random Color
多色塊的其他解決方案?
我們繼續擴充套件延伸一下,本效果的核心還是如何在一個 DIV 下實現多種不同的顏色。
那麼,除了上述的技巧,還有其他方式能夠在一個 DIV 下實現多種不同顏色嗎?
這裡,我們還可以利用內聯元素的 background
展示特性來實現。
什麼意思呢?其實 background
的展示,在 塊級元素狀態 和 內聯元素狀態 下的展示規則是不一樣的。
表現為 display: inline
內聯元素的 background
展現形式與 display: block
塊級元素(或者 inline-block
、flex
、grid
)不一致。
簡單看個例子:
<p>Lorem .....</p><a>Lorem .....</a>
這裡需要注意,<p>
元素是塊級元素,而 <a>
是內聯元素。
我們給它們統一新增上一個從綠色到藍色的漸變背景色:
p, a {
background: linear-gradient(90deg, blue, green);
}
看看效果:
什麼意思呢?區別很明顯:
- 塊級元素的背景整體是一個漸變整體
- 內聯元素的背景效果是以行為單位進行串連的,每一行都是會有不一樣的效果,每行連起來整體組成一個完整的背景效果
基於這一點,我們同樣可以實現單個 DIV 下的多重背景。
舉個例子:
<div class="g-container">
<span>ABCDEFGHIJKL</span>
</div>
div {
width: 300px;
}
span{
color: #000;
font-size: 50px;
line-height: 50px;
letter-spacing: 25px;
word-wrap: break-word;
background: #fc0;
}
此時,我們只設定了一個背景 background: #fc0
,效果如下:
基於上面說的技巧,我們改造一下 background: #fc0
,拆分成多段漸變背景:
span{
//...
background: linear-gradient(
90deg,
#fc0 0 25%,
#0f0 0 50%,
#00f 0 75%,
#f00 0 100%
);
}
這裡,我們每隔 25%,設定了一段不同的顏色,如此一來,整個背景色就變成了 4 塊:
基於這個技巧,我們同樣可以封裝一個 SCSS 函式,用於在單個 DIV 下生成多段色塊。程式碼如下:
@use "sass:string";
@import url('https://fonts.googleapis.com/css2?family=Righteous&family=Ubuntu+Mono&display=swap');
$str: 'QWERTYUIOPASDFGHJKLZXCVBNMabcdefghigklmnopqrstuvwxyz123456789';
$length: str-length($str);
@function randomNum($max, $min: 0, $u: 1) {
@return ($min + random($max)) * $u;
}
@function randomColor() {
@return rgb(randomNum(205, 50), randomNum(255), randomNum(255));
}
@function randomLinear($count) {
$value: '';
@for $i from 0 through ($count - 1) {
$j: $i - 1;
$value: $value + randomColor() + string.unquote(" #{$j * 50}px #{$i * 50}px,");
}
@return linear-gradient(90deg, string.unquote(#{$value}) randomColor() 0 100%);
}
span {
background: randomLinear(36, 50);
}
上面的程式碼,我們實現了一個 randomLinear($count, $width)
的 SCSS 函式,其中:
$count
表示需要的色塊個數$width
表示每個色塊的寬度
如此一來,在一個 300px x 300px
的內聯元素內,我們同樣可以實現多個不同的隨機顏色。利用這個技巧,一樣可以實現單個平面下的隨機文字隨機顏色效果:
剩餘的技巧都是相同的,這裡就不再贅述,此技巧的完整程式碼,你可以戳這裡:CodePen Demo -- Single Div Random Text And Random Color
最後
本文到此結束,希望對你有幫助 ?
更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。
如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。