CSS 如何根據背景色自動切換黑白文字?

XboxYan 發表於 2022-12-29
CSS
歡迎關注我的公眾號:前端偵探

在專案中,經常會碰到背景色不確定的場景,為了讓內容文字足夠清晰可見,文字和背景之間需要有足夠的對比度。換句話說,當背景是深色時,文字為白色,當背景是淺色時,文字為黑色,就像這樣:

image-20221226102604970

通常這種情況,大家可能會透過 js 去計算背景色的深淺度(灰度),演算法是公開的,如果已知顏色的RGB值,那麼可以透過以下方式得到顏色灰度

luma = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255

這樣可以得到一個0~1之間的範圍值,可以根據需求,設定一個閾值,超過表示為淺色,否則為深色。

原理就是這樣,這裡就不多介紹了。

那麼,純 CSS 也能實現這樣的效果嗎?當然可以,而且實現更簡單,一起看看吧

一、CSS 濾鏡實現

實現這個效果需要用到 CSS 濾鏡。

假設有 HTML 是這樣的

<div class="box">
  <span class="txt">前端偵探</span>
</div>

因為要使用濾鏡對文字單獨處理,所以需要額外一層標籤。

然後,容器和文字用同一種顏色表示,目的是讓文字顏色和背景相關聯,可以透過currentColor實現

.box{
  color: #ffeb3b;
  background-color: currentColor;
}

接下來可以想一下,如何讓彩色文字變成黑白

提到黑白,可以想到灰度濾鏡(grayscale),相信大家前幾天都用到過,這樣可以將彩色的文字轉換成灰色

.text{
  filter: grayscale(1)
}

效果如下

image-20221224165239122

這樣文字顏色由原來的黃綠色變成了淺灰色。

但是,這種灰色在現在這種背景下太難看清了,我們需要的是純正的黑色或者白色,現在只是灰色,如何“加強”一下呢?

這時,我們可以用到對比度濾鏡(contrast),在前面的基礎上再疊加一層

.text{
  filter: grayscale(1) contrast(999)
}

這裡的對比度給的比較大,這樣就會極大的增強對比度,黑的更黑,白的更白,如果是淺灰,那就變成白色,如果是深灰,那就變成黑色,效果如下

image-20221224165207221

這樣能還不太明顯,我們把背景色換一下

image-20221224155024179

最後,還差一步,由於前面的操作是將原有顏色經過濾鏡轉換成了和自身相對應的白色或者黑色,但是是相反的,所以需要用到反轉濾鏡(invert),顛倒黑白

.text{
  filter: grayscale(1) contrast(999) invert(1)
}

效果如下

image-20221224155446675

下面用一張圖來表示轉換過程

image-20221224165935410

下面是任意顏色的適配效果,還是挺完美的

Kapture 2022-12-23 at 14.43.29

程式碼很簡單,就這麼一行

.text{
  filter: grayscale(1) contrast(999) invert(1)
}

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

二、CSS 其他思路

除了上面這種方式,還可以透過 CSS 變數來實現,要複雜一些。

這裡簡單介紹一下實現思路

  1. 將顏色RGB值拆分成 3 個獨立的 CSS變數
  2. 透過灰度演算法,用 CSS 計算函式算出灰度
  3. 用得到的灰度和閾值做差值,透過hsl模式轉換成純黑和純白

有興趣的可以參考張鑫旭老師的這篇文章:CSS前景背景自動配色技術簡介,可以看到,整體實現和 js 邏輯幾乎是一致的,下面是完整實現

另外可以參考之前這篇文章:CSS 變數自動變色技術
:root {
  /* 定義RGB變數 */
  --red: 44;
  --green: 135;
  --blue: 255;
  /* 文字顏色變色的臨界值,建議0.5~0.6 */
  --threshold: 0.5;
}

.btn {
  /* 按鈕背景色就是基本背景色 */
  background: rgb(var(--red), var(--green), var(--blue));

  /** 
   * 使用sRGB Luma方法計算灰度(可以看成亮度)
   * 演算法為:
   * lightness = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
  */
  --r: calc(var(--red) * 0.2126);
  --g: calc(var(--green) * 0.7152);
  --b: calc(var(--blue) * 0.0722);
  --sum: calc(var(--r) + var(--g) + var(--b));
  --lightness: calc(var(--sum) / 255);
  
  /* 設定顏色 */
  color: hsl(0, 0%, calc((var(--lightness) - var(--threshold)) * -999999%));
}

效果如下

auto-color-button.gif (228×225) (zhangxinxu.com)

相比前面的實現而言,實現更加靈活,可以少一層標籤。

另外,CSS 正在起草一個顏色對比函式color-contrast,可以從幾個顏色中自動選擇對比度最高的那個,實現是這樣的

.text-contrast-primary {
  color: color-contrast(var(--theme-primary) vs white, black);
}

不過,現在還沒有任何瀏覽器支援。

image-20221224174008923

如果將來支援了,這將是終極解決方案。

三、優缺點總結

總的來說,在color-contrast函式支援之前,我更推薦 CSS 濾鏡方式,有以下幾點好處

  1. 程式碼簡潔,就一行程式碼,3 個濾鏡
  2. 對顏色格式無任何要求,無需轉換成 RGB模式
  3. 無需瞭解顏色演算法,對設計更為友好

當然,也是存在一些缺點

  1. 需要單獨一層標籤,使用場景可能有限制
  2. 對顏色敏感度較高,不然無從下手
  3. 顏色轉換有限制,最終只能是黑白,其他顏色就無能為力了

下面來回顧一下用到的3個濾鏡,總結一下

  1. 灰度濾鏡(grayscale),可以將彩色的文字轉換成灰色
  2. 對比度濾鏡(contrast),可以極大的增強對比度,黑的更黑,白的更白,如果是淺灰,那就變成白色,如果是深灰,那就變成黑色
  3. 反轉濾鏡(invert),可以翻轉顏色,顛倒黑白

重新體會顏色轉換過程

image-20221224165935410

你記住了嗎?最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤

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