【vue】聊一聊Element UI的自定義主題顏色

背景

ElementUI組件庫(kù)相信大家一定都接觸過(guò)候齿。但是自定義主題顏色的需求有接觸過(guò)的應(yīng)該不多颗搂,至少我到今天是沒(méi)有遇到類似的需求拍嵌。之所以講這個(gè)需求靴跛,是因?yàn)樵谖覀€(gè)人開(kāi)發(fā)的開(kāi)源項(xiàng)目中有做到這個(gè)需求谦絮,所以在這里和大家聊一聊我的實(shí)現(xiàn)幢泼。

CSS變量

在此之前我們需要先了解一下CSS變量:它是自定義屬性(有時(shí)候也被稱作CSS變量或者級(jí)聯(lián)變量)是由CSS作者定義的紧显,它包含的值可以在整個(gè)文檔中重復(fù)使用。由自定義屬性標(biāo)記設(shè)定值缕棵,由var()函數(shù)來(lái)獲取值孵班。

/* 設(shè)定值 */
:root {
    --background-color: #FF0000;
}
/* 獲取值 */
div {
    background-color: var(--background-color);
}

Element主題色

Element官方文檔我們可以它的整個(gè)組件庫(kù)都是使用的CSS變量實(shí)現(xiàn)的。并且它提供了四種方式來(lái)修改樣式變量招驴,具體方式可以參考官方文檔篙程。

我這邊主要介紹的就是Element通過(guò)js控制CSS變量設(shè)置的方式。那就會(huì)有兩個(gè)問(wèn)題:

如何通過(guò)js獲取到CSS變量

js獲取CSS變量那就要介紹這個(gè)方法getComputedStyle():它返回一個(gè)對(duì)象别厘,該對(duì)象在應(yīng)用活動(dòng)樣式表并解析這些值可能包含的任何基本計(jì)算后報(bào)告元素的所有 CSS 屬性的值虱饿。它有兩個(gè)參數(shù):第一個(gè)參數(shù)就是用于獲取計(jì)算樣式的Element;第二個(gè)參數(shù)是可選參數(shù)丹允,是指定一個(gè)要匹配的偽元素的字符串郭厌。返回的style是一個(gè)實(shí)時(shí)的 CSSStyleDeclaration 對(duì)象,當(dāng)元素的樣式更改時(shí)雕蔽,它會(huì)自動(dòng)更新本身折柠。CSSStyleDeclaration對(duì)象中的getPropertyValue()接口返回一個(gè)DOMString,其中包含請(qǐng)求的CSS屬性的值批狐。

const element = document.documentElement;
const style = window.getComputedStyle(element);
const value = style.getPropertyValue('`background-color`');

如何通過(guò)js設(shè)置CSS變量

js設(shè)置CSS變量那就要介紹CSSStyleDeclaration對(duì)象中的setProperty()接口扇售,它有三個(gè)參數(shù):第一個(gè)參數(shù)是一個(gè)DOMString,代表被更改的CSS屬性嚣艇;第二個(gè)參數(shù)是一個(gè)可選參數(shù)也是一個(gè)DOMString承冰,含有新的屬性值。如果沒(méi)有指定食零,則當(dāng)作空字符串困乒;第三個(gè)參數(shù)是一個(gè)可選參數(shù),是一個(gè) DOMString 允許設(shè)置 "important" CSS 優(yōu)先級(jí)贰谣。如果沒(méi)有指定娜搂,則當(dāng)作空字符串。

有了以上兩個(gè)方法那我們就可以通過(guò)js直接修改Element的主題色了吱抚,簡(jiǎn)單實(shí)現(xiàn)一下:

<template>
  <div class="container">
    <el-button>Default</el-button>
    <el-button v-for="item in types" :key="item" :type="item">{{ item }}</el-button>
    <hr>
    <el-radio-group v-model="radio" @change="radioChangeHandle">
      <el-radio v-for="item in types" :key="item" :label="item" size="large">{{ item }}</el-radio>
    </el-radio-group>
    <br>
    <el-color-picker v-model="color" @change="colorChangeHandle" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

const el = document.documentElement

const types = ref(['primary', 'success', 'info', 'warning', 'danger'])

const radio = ref('primary') 

const color = ref('')

const radioChangeHandle = () => {
  const value = getComputedStyle(el).getPropertyValue(`--el-color-${radio.value}`)
  color.value = value
}

const colorChangeHandle = (value) => {
  el.style.setProperty(`--el-color-${radio.value}`, value)
}
</script>

<style>
.container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -100%);
  width: 50%;
  text-align: center;
}
</style>
image.png

通過(guò)以上方式修改了之后百宇,所有的組件的顏色都已經(jīng)修改了,并且已經(jīng)達(dá)到了效果秘豹。但是其中還存在一個(gè)問(wèn)題携御,以el-button組件舉例。每個(gè)按鈕組件都會(huì)有個(gè)hover的顏色,雖然顯示的顏色已經(jīng)修改了啄刹,但是hover的顏色還是原來(lái)的顏色涮坐,這是一個(gè)嚴(yán)重的Bug。原因是按鈕的組件hover的顏色使用了其CSS變量誓军。

image.png
image.png

顏色的HEX格式

顏色的HEX格式是#+六位數(shù)字/字母膊升,其中六位數(shù)字/字母是一種十六進(jìn)制的表達(dá)方式。這六位分別兩個(gè)一組谭企,從左到右分別表示廓译、藍(lán)债查。00表示最小非区,十進(jìn)制是0FF表示最大盹廷,十進(jìn)制是255征绸。通俗點(diǎn)講,某個(gè)顏色的數(shù)值越大俄占,包含這個(gè)顏色就越多管怠。如:#000000-黑色、#FFFFFF-白色缸榄、#FF0000-紅色渤弛、#00FF00-綠色、#0000FF-藍(lán)色甚带。

HEX格式顏色變亮她肯、變暗

通過(guò)上文的介紹,我們可以知道Elementhover顏色變亮了鹰贵,即顏色的數(shù)值變大了晴氨,那我們只要對(duì)要修改的顏色數(shù)值變大即可。那就需要用到以下的方法:

HEX格式轉(zhuǎn)RGB格式

hex2Rgb(color) {
  color = color.replace('#', '')
  const result = color.match(/../g)
  for (let i = 0; i < 3; i++) {
    result[i] = parseInt(result[i], 16)
  }
  return result
}

RGB格式轉(zhuǎn)HEX格式

rgb2Hex(r, g, b) {
  const hexs = [r.toString(16), g.toString(16), b.toString(16)]
  for (let i = 0; i < 3; i++) {
    if (hexs[i].length === 1) {
      hexs[i] = '0' + hexs[i]
    }
  }
  const result = '#' + hexs.join('')
  return result
}

使顏色變亮

lighten(color, level) {
  const rgb = hex2Rgb(color)
  for (let i = 0; i < 3; i++) {
    rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
  }
  const result = rgb2Hex(rgb[0], rgb[1], rgb[2])
  return result
}

使顏色變暗

darken(color, level) {
  const rgb = hex2Rgb(color)
  for (let i = 0; i < 3; i++) {
    rgb[i] = Math.floor(rgb[i] * (1 - level))
  }
  const result = rgb2Hex(rgb[0], rgb[1], rgb[2])
  return result
}

解決問(wèn)題

有了上文的幾個(gè)方法碉输,我們就可以在修改主色的時(shí)候?qū)?duì)應(yīng)的其他CSS變量進(jìn)行變亮或者變暗即可籽前。一般這種主題都是會(huì)存儲(chǔ)瀏覽器Storage中,大家可以結(jié)合實(shí)際情況配合vuex或者pinia使用敷钾。這里就不展開(kāi)聊了枝哄。貼上完整代碼:

// utils.js
export function hex2Rgb(color) {
  color = color.replace('#', '')
  const result = color.match(/../g)
  for (let i = 0; i < 3; i++) {
    result[i] = parseInt(result[i], 16)
  }
  return result
}
export function rgb2Hex(r, g, b) {
  const hexs = [r.toString(16), g.toString(16), b.toString(16)]
  for (let i = 0; i < 3; i++) {
    if (hexs[i].length === 1) {
      hexs[i] = '0' + hexs[i]
    }
  }
  const result = '#' + hexs.join('')
  return result
}
export function lighten(color, level) {
  const rgb = hex2Rgb(color)
  for (let i = 0; i < 3; i++) {
    rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i])
  }
  const result = rgb2Hex(rgb[0], rgb[1], rgb[2])
  return result
}
export function darken(color, level) {
  const rgb = hex2Rgb(color)
  for (let i = 0; i < 3; i++) {
    rgb[i] = Math.floor(rgb[i] * (1 - level))
  }
  const result = rgb2Hex(rgb[0], rgb[1], rgb[2])
  return result
}
// index.vue
<template>
  <div class="container">
    <el-button>Default</el-button>
    <el-button v-for="item in types" :key="item" :type="item">{{ item }}</el-button>
    <hr>
    <el-radio-group v-model="radio" @change="radioChangeHandle">
      <el-radio v-for="item in types" :key="item" :label="item" size="large">{{ item }}</el-radio>
    </el-radio-group>
    <br>
    <el-color-picker v-model="color" @change="colorChangeHandle" color-format="hex" :show-alpha="false" />
  </div>
</template>

<script setup>
import { ref } from 'vue'

import { lighten, darken } from '@/utils'

const el = document.documentElement

const types = ref(['primary', 'success', 'info', 'warning', 'danger'])

const radio = ref('primary') 

const color = ref(getComputedStyle(el).getPropertyValue(`--el-color-${radio.value}`))

const radioChangeHandle = () => {
  const value = getComputedStyle(el).getPropertyValue(`--el-color-${radio.value}`)
  color.value = value
}

const colorChangeHandle = (value) => {
  el.style.setProperty(`--el-color-${radio.value}`, value)
  for (let i = 1; i <= 9; i++) {
    el.style.setProperty(`--el-color-${radio.value}-light-${ i }`, lighten(value, i / 10))
    el.style.setProperty(`--el-color-${radio.value}-dark-${ i }`, darken(value, i / 10))
  }
}
</script>

<style>
.container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -100%);
  width: 50%;
  text-align: center;
}
</style>

以上就是本次分享的內(nèi)容,附上源碼闰非。

感謝看官看到這里膘格,如果覺(jué)得文章不錯(cuò)的話峭范,可以給小生的幾個(gè)開(kāi)源項(xiàng)目點(diǎn)個(gè)Star?财松!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辆毡,更是在濱河造成了極大的恐慌菜秦,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舶掖,死亡現(xiàn)場(chǎng)離奇詭異球昨,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)眨攘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)主慰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鲫售,你說(shuō)我怎么就攤上這事共螺。” “怎么了情竹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵藐不,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我秦效,道長(zhǎng)雏蛮,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任阱州,我火速辦了婚禮挑秉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苔货。我一直安慰自己衷模,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布蒲赂。 她就那樣靜靜地躺著阱冶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滥嘴。 梳的紋絲不亂的頭發(fā)上木蹬,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音若皱,去河邊找鬼镊叁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛走触,可吹牛的內(nèi)容都是我干的晦譬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼互广,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼敛腌!你這毒婦竟也來(lái)了卧土?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤像樊,失蹤者是張志新(化名)和其女友劉穎尤莺,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體生棍,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颤霎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涂滴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片友酱。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖柔纵,靈堂內(nèi)的尸體忽然破棺而出粹污,到底是詐尸還是另有隱情,我是刑警寧澤首量,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布壮吩,位于F島的核電站,受9級(jí)特大地震影響加缘,放射性物質(zhì)發(fā)生泄漏鸭叙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一拣宏、第九天 我趴在偏房一處隱蔽的房頂上張望沈贝。 院中可真熱鬧,春花似錦勋乾、人聲如沸宋下。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)学歧。三九已至,卻和暖如春各吨,著一層夾襖步出監(jiān)牢的瞬間枝笨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工揭蜒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留横浑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓屉更,卻偏偏與公主長(zhǎng)得像徙融,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瑰谜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容