CSS 顏色混合的N種方式

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

在專案中經常會碰到需要鄰近色的場景,比如將一個顏色變深(淺)一點,下面是一個按鈕的選中態

image.png

如果出現一種顏色就定義一個變數,每次都要維護多個顏色變數太麻煩了。有沒有辦法只用一個顏色呢?在這裡也就是,如何將一個顏色變淺一點?這樣做的好處是,如果需要更換主題色,只用修改一個變數就行了,如下

image.png

下面盤點我用過的一些方法

一、透明度

這應該是最容易想到的方式。將一個元素透明度降低不就顏色變淺了嗎?

假設 HTML是這樣的(下同)

<button style="--primary-color: #3981E6">#3981E6</button>

不過這種方式需要藉助單獨一層標籤,通常可以用偽元素生成,關鍵實現如下

button::before{
  content: '';
  background: var(--primary-color);
  opacity: 0.2
}

效果還是非常不錯的,下面是多種主題色的效果

image.png

不過這種方式也有侷限,比如偽元素不夠用怎麼辦?還有絕對定位引起的層級問題

沒關係,還有其他方式,接著往下看

二、多重背景

大家可能都知道,CSS3 背景是支援多重背景的,並且層級是越來越低的

image.png

因此,我們可以在主題色上覆蓋一層半透明的白色,依然可以將原有顏色“減淡”

由於這裡是背景圖,所以需要用到漸變,而不是顏色。比如希望主題色減淡到自身的20%,可以覆蓋80%透明度的白色,實現如下

button{
  background: linear-gradient(rgba(255,255,255,.8),rgba(255,255,255,.8)), /*半透明白色*/
    linear-gradient(var(--primary-color), var(--primary-color));
}

這樣就無需藉助額外的標籤或者偽元素了,效果如下

image.png

不過這種方式也有一些缺陷,比如僅適合背景層,如果希望box-shadowoutline這些就不行,這些屬性沒法疊加多層背景。

三、動畫

這個方式在之前這篇文章中有詳細介紹:

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

主要原理是動畫播放次數也是支援小數的,比如設定一個從藍色到白色的動畫,播放次數為0.8,那麼在播放到80%的地方就停下來了,這樣就得到了顏色減淡的效果,示意如下

image.png

具體實現如下

button{
  animation: lighterBackgroundColor .001s 0.8 linear forwards;
  /*播放次數為0.8*/
}
@keyframes lighterBackgroundColor {
  from {
    background-color: var(--primary-color)
  }
  to{
    background-color: #fff
  }
}

效果也很棒

image.png

相比前一種方式,就沒有背景的限制了,任意屬性都可以,但是每出現一個屬性就需要單獨一個動畫(因為動畫變化的就是屬性本身),如果要加一個減淡後的outline-color,應該要這麼實現

button{
  animation: lighterBackgroundColor .001s 0.8 linear forwards, 
    lighterOutlineColor .001s 0.8 linear forwards; /*outline*/
  /*播放次數為0.8*/
}
@keyframes lighterBackgroundColor {
  from {
    background-color: var(--primary-color)
  }
  to{
    background-color: #fff
  }
}
/*設定一個outline的動畫*/
@keyframes lighterOutlineColor {
  from {
    outline-color: var(--primary-color)
  }
  to{
    outline-color: #fff
  }
}

太繁瑣了,有沒有簡單一點的方法呢?

四、自定義屬性動畫

上面將屬性作為動畫有點浪費,因為變化值都是一樣的,有沒有辦法複用呢?

當然可以,將 CSS 變數作為動畫物件,比如--lighterColor


button{
  animation: lighterColor .001s 0.8 linear forwards;
  /*播放次數為0.8*/
}
@keyframes lighterColor {
  from {
    --lighterColor: var(--primary-color)
  }
  to{
    --lighterColor: #fff
  }
}

但是,僅僅這樣是不夠的,動畫並不認識這樣的變數,根本不會有動畫(就像display一樣)

為了讓自定義變數也支援動畫,需要透過@property定義一下

@property - CSS:層疊樣式表 | MDN (mozilla.org)
@property --lighterColor {
  syntax: '<color>';
  inherits: false;
  initial-value: #fff;
}

相比前面的方式,但是適用性更佳,--lighterColor已經是一個獨立的變數了,可以用在任意屬性上,比如加個outline

button{
  background-color: var(--lighterColor);
  outline: 4px solid var(--lighterColor);
}

可以看到,outline也輕易的實現了顏色減淡

image.png

缺點就是,相容性欠佳,目前firefox還不支援

五、color-mix()

最後介紹一個最近正式支援(Chrome 110+)的顏色混合函式:color-mix()

這個算是官方的解決方案了,如果這個普遍支援了,前面的方法都可以不用了,下面簡單介紹一下

color-mix(in lch, peru 40%, lightgoldenrod);
color-mix(in srgb, #34c9eb 20%, white);

前面的in lch表示色彩空間,我們一般只用srgb就足夠了,後面的兩個顏色就需要混合的顏色了。

這裡的百分比就是混合比例了,如果我們要實現減淡80%的操作,可以將主題色的比例設定為20%,白色會自動填充剩餘比例,如下

button{
  --lighterColor: color-mix(in srgb, var(--primary-color) 20%, #fff);
  background-color: var(--lighterColor);
  outline: 4px solid var(--lighterColor);
}

效果如下(Chrome 110+)

image.png

目前還不適合使用,過兩年再說吧?

下面是所有方案的效果對比,基本是一致的

image.png

完整 demo 可以訪問以下任意連結

六、總結一下優缺點

以上共介紹了5種不同的顏色混合實現方式,各有優缺點,下面分別從以下幾個方面比較一下

  • 實現成本:實現思路的複雜度,是否容易想到
  • 適應性:能否適應各種場景
  • 程式碼複用性:實現是否囉嗦,是否需要額外標籤
  • 相容性:能否大規模使用
實現成本適應性程式碼複用性相容性
透明度⭐️(低)⭐️(差)⭐️(低)⭐️⭐️⭐️(好)
多重背景⭐️⭐️⭐️⭐️⭐️⭐️
動畫⭐️⭐️⭐️(高)⭐️⭐️⭐️⭐️⭐️⭐️⭐️
自定義屬性動畫⭐️⭐️⭐️⭐️⭐️(強)⭐️⭐️⭐️⭐️⭐️
color-mix⭐️⭐️⭐️⭐️⭐️⭐️⭐️(高)⭐️(差)

總的來說,自定義屬性動畫在各方面是比較推薦的,如果不考慮firefox的話基本可以放心使用了,其他方式也可以根據實際需求自行選擇,哪個方便用哪個。

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

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

本文參與了SegmentFault 思否寫作挑戰賽,歡迎正在閱讀的你也加入。

相關文章