核心
- 使用CSS變量, 準(zhǔn)備兩套CSS顏色, 一套是在 light模式下的顏色,一套是在dark模式下的顏色
- dark模式下的 CSS 權(quán)重要比 light 模式下的權(quán)重高, 不然當(dāng)我們給html添加自定義屬性
[data-theme='dark']
的時候, dark模式權(quán)重比light低,會一直不起效果 - 當(dāng)我們點擊 dark 模式的時候, 給 html 設(shè)置自定義屬性
[data-theme='dark']
- 當(dāng)我們點擊 light 模式的時候, 給 html 設(shè)置自定義屬性
[data-theme='light']
- 在 dark 模式下, 會匹配到
html[data-theme='dark']
選擇器下的樣式 - 在 light 模式下,由于我們沒有設(shè)置
html[data-theme='light']
的方案, 那么他就匹配:root(即html)下的樣式
兩套樣式代碼大概如下(列了一部分):
:root {
--color-body-bg: #ffffff;
--color-text: #000;
--color-secondary-bg-for-transparent: rgba(209, 209, 214, 0.28);
}
html[data-theme='dark'] {
--color-body-bg: #222222;
--color-text: #ffffff;
--color-primary-bg-for-transparent: rgba(255, 255, 255, 0.12);
}
然后我們點擊的時候,通過
let theme = 'light' // light / dark
document.documentElement.setAttribute('data-theme', theme)
這樣就能實現(xiàn)簡單的更換膚色功能了
什么? 你以為這就完了?好戲剛開始
跟隨系統(tǒng)顏色
首先利用Window
的 'matchMedia()' 方法返回一個新的 MediaQueryList
對象,表示指定的媒體查詢 (en-US)字符串解析后的結(jié)果。
如運行媒體查詢(max-width: 600px)
并在;中顯示MediaQueryList
的matches
屬性值。如果視口的寬度小于或等于 600 像素,則輸出將為 true,而如果窗口的寬度大于此寬度,則將輸出 false。
let mql = window.matchMedia('(max-width: 600px)');
document.querySelector(".mq-value").innerText = mql.matches; //此時小于或等于600像素時span 里面的結(jié)果為false
利用prefers-color-scheme
[CSS媒體特性] 用于檢測用戶是否有將系統(tǒng)的主題色設(shè)置為亮色或者暗色。
.day { background: #eee; color: black; }
.night { background: #333; color: white; }
@media (prefers-color-scheme: dark) {
.day.dark-scheme { background: #333; color: white; }
.night.dark-scheme { background: black; color: #ddd; }
}
@media (prefers-color-scheme: light) {
.day.light-scheme { background: white; color: #555; }
.night.light-scheme { background: #eee; color: black; }
}
兩者相結(jié)合
把matchMedia()
和prefers-color-scheme
結(jié)合在一起, 我們就可以通過 js 去給系統(tǒng)顏色為dark或 light 的情況下更換對應(yīng)的 html自定義屬性, 即[data-theme='dark']
或[data-theme='light']
首先,我們先去獲取主題顏色, 我們還沒設(shè)置的時候,就默認(rèn)是系統(tǒng)顏色, 設(shè)置了就把他存儲起來,下次直接獲取這個顏色
// 獲取主題變量
let appearance = ref(localStorage.getItem('appearance') || 'auto')
// 查詢當(dāng)前系統(tǒng)主題顏色
const match = window.matchMedia("(prefers-color-scheme: dark)")
// 如果主題變量為 auto, 則跟隨系統(tǒng)主題
if (appearance.value === 'auto') {
followSystem()
} else {
document.documentElement.setAttribute('data-theme', appearance.value)
}
function followSystem() {
// 當(dāng)前系統(tǒng)顏色是亮色還是暗色 , 設(shè)置對應(yīng)的html[data-theme= 'dark' 或者'light']
const theme = match.matches ? 'dark' : 'light'
document.documentElement.setAttribute('data-theme', theme)
}
// 監(jiān)聽系統(tǒng)主題變化,電腦主題發(fā)生改變的時候就調(diào)用followSystem函數(shù)
match.addEventListener('change', followSystem)
封裝成一個hooks
暴露出一個 useThemeColor函數(shù), 返回一個對象, 對象里面返回我們的主題變量
/ 獲取主題變量
let appearance = ref(localStorage.getItem('appearance') || 'auto')
// 查詢當(dāng)前系統(tǒng)主題顏色
const match:MediaQueryList = window.matchMedia("(prefers-color-scheme: dark)")
// 監(jiān)聽系統(tǒng)主題變化
match.addEventListener('change', followSystem)
function followSystem() {
const theme = match.matches ? 'dark' : 'light'
document.documentElement.setAttribute('data-theme', theme)
}
watchEffect(() => {
// 如果主題變量為 auto, 則跟隨系統(tǒng)主題
if (appearance.value === 'auto') {
followSystem()
} else {
document.documentElement.setAttribute('data-theme', appearance.value)
}
})
export default function useThemeColor() {
return {
appearance,
}
}
使用hooks
導(dǎo)入我們export出來的函數(shù)
import useThemeColor from '../hooks/useThemeColor'
使用函數(shù),注意, 這里返回的 apprance 已經(jīng)是一個響應(yīng)式數(shù)據(jù)了
const { appearance } = useThemeColor()
使用 v-model 綁定apprance,直接使用apprance , 當(dāng)我們切換顏色的時候, 就會調(diào)用watchEffect里面的函數(shù), 達(dá)到一鍵換膚效果