背景
Element
UI組件庫(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>
通過(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變量
誓军。
顏色的HEX格式
顏色的HEX
格式是#
+六位數(shù)字/字母
膊升,其中六位數(shù)字/字母
是一種十六進(jìn)制的表達(dá)方式。這六位分別兩個(gè)一組谭企,從左到右分別表示紅
廓译、綠
、藍(lán)
债查。00
表示最小非区,十進(jìn)制是0
;FF
表示最大盹廷,十進(jìn)制是255
征绸。通俗點(diǎn)講,某個(gè)顏色的數(shù)值越大俄占,包含這個(gè)顏色就越多管怠。如:#000000
-黑色、#FFFFFF
-白色缸榄、#FF0000
-紅色渤弛、#00FF00
-綠色、#0000FF
-藍(lán)色甚带。
HEX格式顏色變亮她肯、變暗
通過(guò)上文的介紹,我們可以知道Element
的hover
顏色變亮了鹰贵,即顏色的數(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?财松!
- 基于 Vue3 + Element-plus 管理后臺(tái)基礎(chǔ)功能框架
- 基于 Vue3 + Element-plus + websocket 即時(shí)聊天系統(tǒng)
- 基于 node 開(kāi)發(fā)的后端服務(wù):https://github.com/gmingchen/node-server