妙用 CSS 動畫來實現顏色加深、減淡等混合操作

XboxYan 發表於 2023-01-03
CSS
歡迎關注我的公眾號:前端偵探

在上一篇 CSS 如何根據背景色自動切換黑白文字?中,講述了文字自適應背景色的一些小技巧,不過還存在一定侷限性,比如:如果是背景是漸變色該怎麼辦?

image.png

很容易想到的思路是將兩個漸變色取過渡中間色,然後再透過前面的方式轉換就行了

image.png

那麼問題來了,有沒有辦法透過 CSS 實現中間顏色的獲取呢?今天來一起探討這個問題,聊一聊關於顏色合成的相關技巧。

一、你可能不知道 CSS 動畫小技巧

想必大家都用過 CSS 動畫,比如

@keyframes color {
  from {
    color: yellow
  }
  to{
    color: deeppink
  }
}
.text{
  animation: color 1s linear forwards;
}

這樣就得到了一個顏色從yellowdeeppink的動畫

Kapture 2022-12-28 at 15.49.54.gif

這個沒什麼好說的。

預設情況下,動畫會播放 1 次後結束,然後設定了forwards,會保留在最後一幀狀態。

那麼,如果動畫只播放一半,是不是就正好處於兩者顏色的中間?其實,播放次數也可以是小數的,比如可以將播放次數設定為0.5次,就像這樣

.text{
  animation: color 1s .5 linear forwards;
}

效果如下

Kapture 2022-12-28 at 15.56.58.gif

由於只播放了一半,所以到中間的橙色就停止了下來。

值得一提的是,透過這種方式得到的顏色,也是可以用 JS 去獲取的

image.png

那麼,利用這個特性,可以實現顏色的各種合成效果。

二、漸變背景下的文字自適應

回到前面的問題,如果是漸變背景,該如何實現自動切換黑白文字呢?

假設漸變的兩種顏色分別是--c1--c2

<div class="box" style="--c1: #ffeb3b; --c2: deeppink">
  <span class="txt">前端偵探</span>
</div>

那麼根據上一節的方法,可以將動畫改造成這樣

@keyframes color {
  from {
    color: var(--c1)
  }
  to{
    color: var(--c2)
  }
}

我們這裡只是需要獲取一下顏色,並不需要動畫,所以可以將動畫時長設定為很小的一個數,比如0.001s

.txt{
  animation: color .001s .5 linear forwards;
}

這樣文字顏色就自動變成了漸變顏色的中間值,如下

image.png

然後再應用濾鏡,將文字轉換成黑色或者白色

.txt{
  animation: color .001s .5 linear forwards;
  filter: grayscale(1) contrast(9999) invert(1);
}

效果如下

image.png

也能完美適配任意漸變色

Kapture 2022-12-28 at 16.26.38.gif

完整程式碼可以檢視以下任意連結

三、顏色的加深和減淡

再來看一個更加實用的例子,顏色的加深和減淡。通常用於主題色的生成,比如給定一個主題色,生成一系列和它相匹配的鄰近色。下面是顏色逐漸減淡,最終變為白色的色階圖

image.png

根據上面的原理,可以很輕易的實現這樣一個效果

假設 HTML 是這樣的,每個方塊給一個不同的 CSS 變數--l

<div class="box" style="--l:0"></div>
<div class="box" style="--l:0.2"></div>
<div class="box" style="--l:0.4"></div>
<div class="box" style="--l:0.6"></div>
<div class="box" style="--l:0.8"></div>
<div class="box" style="--l:1"></div>

然後建立一個從主題色到白色的動畫,根據這個變數,讓動畫執行不同的次數

.box{
  animation: lighterBackgroundColor .001s var(--l) linear forwards;
}
@keyframes lighterBackgroundColor {
  from {
    background-color: var(--primary-color)
  }
  to{
    background-color: #fff
  }
}

這樣就可以生成同種顏色,不同深淺度的主題色了

有同學可能會說像 sassless這些不也能實現嗎?其實不然,這些都是前處理器,生成以後就不能再變了,而這種方式是實時繪製的,可以實現修改,如下

Kapture 2022-12-28 at 17.25.08.gif

完整程式碼可以檢視以下任意連結

如果將這種技巧用到實際專案中也是非常完美,下面是不同主題色下的預覽效果

image.png

選中背景色就是減淡80%後的顏色

.item.current{
  border-color: var(--primary-color);
  animation: lighterBackgroundColor .001s .8 linear forwards;
}

原理是完全相同的,這裡就不詳細介紹了,完整程式碼可以檢視以下任意連結

除此之外,根據需求,還可以透過顏色透明度的變化來生成特定透明度的主題色

@keyframes OpacityBackgroundColor {
  from {
    background-color: var(--primary-color)
  }
  to{
    background-color: rgba(0,0,0,0)
  }
}

四、未來最期待的幾個顏色處理函式

官方也看到了這種需求,因此在 CSS Color Module Level 5中起草了幾個關於顏色合成的函式,這裡簡單介紹一下

首先是顏色混合color-mix,將兩種顏色按照一定的比例進行融合

color-mix(in srgb, white, blue);

這表示whiteblue按照各自 50% 進行混合,最終會得到紫色rgb(50% 50% 100%)

如果要控制混合比例,可以這樣

color-mix(in srgb, white 20%, blue);

還有一個叫做相對顏色relative color ,其實是對原有的顏色函式進行了補充,根據我的理解,可以將這個特性想象成 JS 中的解構賦值

hsl(from var(--accent) h s calc(l - 20%))

例如這個表示將顏色--accent分解成hsl三個變數,然後對其中的l,也就是亮度減少20%,也就達到了顏色變暗的目的

多麼令人興奮的特性!目前這兩個特性僅在 safari 15+試驗性功能開啟支援(😂終於不拖後腿了)

image.png

五、總結一下

以上就是本文全部內容了,主要是利用 CSS 動畫的過渡特性,間接達到了顏色混合的目的,下面是一些要點:

  1. CSS 動畫的次數也能設定成小數,比如 0.5 表示動畫只執行到一半
  2. 兩個顏色的中間色就是顏色動畫執行到一半的狀態
  3. 顏色減淡可以看成是主題色到白色的動畫,加深則是黑色
  4. 官方已經正在起草 CSS 顏色合成相關函式,目前僅 Safari 試驗性支援

你學會了嗎?總的來說,在color-mix到來之前,這樣一個小技巧還算是不錯的解決方案。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

歡迎關注我的公眾號:前端偵探