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

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

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

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

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

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

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

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

這個沒什麼好說的。

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

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

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

效果如下

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

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

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

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

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

假設漸變的兩種顏色分別是--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;
}

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

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

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

效果如下

也能完美適配任意漸變色

完整代碼可以查看以下任意鏈接

三、顏色的加深和減淡

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

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

假設 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這些不也能實現嗎?其實不然,這些都是預處理器,生成以後就不能再變了,而這種方式是實時繪製的,可以實時修改,如下

完整代碼可以查看以下任意鏈接

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

選中背景色就是減淡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[10] 中起草了幾個關於顏色合成的函數,這裏簡單介紹一下

首先是顏色混合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+試驗性功能開啓支持(😂終於不拖後腿了)

五、總結一下

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

  1. CSS 動畫的次數也能設置成小數,比如 0.5 表示動畫只運行到一半

  2. 兩個顏色的中間色就是顏色動畫運行到一半的狀態

  3. 顏色減淡可以看成是主題色到白色的動畫,加深則是黑色

  4. 官方已經正在起草 CSS 顏色合成相關函數,目前僅 Safari 試驗性支持

參考資料

[1]

CSS auto-gradient-color (juejin.cn): https://code.juejin.cn/pen/7182116774291177509

[2]

CSS auto-gradient-color (codepen.io): https://codepen.io/xboxyan/pen/wvxMjyg

[3]

CSS auto-gradient-color (runjs.work): https://runjs.work/projects/665883ec8f554759

[4]

CSS lighten (juejin.cn): https://code.juejin.cn/pen/7182130832608346146

[5]

CSS lighten (codepen.io): https://codepen.io/xboxyan/pen/PoBZapJ

[6]

CSS lighten (runjs.work): https://runjs.work/projects/cff8abbeb12e4950

[7]

CSS color-mix (juejin.cn): https://code.juejin.cn/pen/7182134966540009529

[8]

CSS color-mix (codepen.io): https://codepen.io/xboxyan/pen/jOpWKxX

[9]

CSS color-mix (runjs.work): https://runjs.work/projects/3d34d06657d0411c

[10]

CSS Color Module Level 5: https://www.w3.org/TR/css-color-5

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/S6cQ4ccr8geiskYVEJHufQ