Appearance
多品牌、亮色暗色模式
思路
亮色、暗黑模式:
使用CSS变量来定义颜色,定义两套在亮色、暗黑的CSS变量,变量名称相同,值不同。在书写样式时不使用硬编码,而是使用CSS变量代替,这样通过JS给HTML设置对应的自定义属性或类名就实现亮色与暗黑模式的切换。 可以通过监听window.matchMedia('(prefers-color-scheme: dark)')来判断当前浏览器返回的模式,从而切换对应的主题模式
多品牌:
也是使用CSS变量,通过定义多套亮色、暗黑模式实现
js
theme
用户设置的主题,system、light、dark
currentTeme
当前主题,light、dark 只有亮色、暗色
brand
当前品牌注意点: 通常情况下window.matchMedia('(prefers-color-scheme: dark)')返回的是浏览器的模式,而不是系统的模式。单个别情况下也可能不是,目前发现QQ浏览器就是返回的系统的模式
实现
demo在m-web项目中
vue
<template>
<div>
<h1>主题模式</h1>
<a-radio-group v-model:value="theme" :options="themeList" option-type="button" @change="handleChangeTheme" />
<div>
主题:{{ theme }}
</div>
<a-radio-group v-model:value="brand" :options="brandList" option-type="button" @change="handleChangeBrand" />
<div class="test_theme">
测试主题
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
// 获取系统偏好
const getSystemTheme = () => {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
/*
* 当前模式
* light: 亮色模式
* dark: 暗色模式
* system: 跟随系统
*/
const theme = ref(localStorage.getItem('theme') || 'system');
// 当前主题
const currentTheme = ref(theme.value === 'system' ? getSystemTheme() : theme.value);
// 主题
const brand = ref(localStorage.getItem('brand') || 'default');
/*
* 主题列表
* light: 明亮
* dark: 暗黑
* system: 跟随系统
*/
const themeList = ref([
{
label: '跟随系统',
value: 'system',
},
{
label: '明亮',
value: 'light',
},
{
label: '暗黑',
value: 'dark',
},
]);
// 品牌列表
const brandList = ref([
{
label: '默认',
value: 'default',
},
{
label: '蓝色',
value: 'blue',
},
{
label: '绿色',
value: 'green',
},
{
label: '黄色',
value: 'yellow',
},
{
label: '红色',
value: 'red',
},
]);
/**
* 切换品牌
* 1. 设置品牌
*/
const setBrand = () => {
localStorage.setItem('brand', brand.value);
const root = document.documentElement;
root.dataset.brand = brand.value;
};
// 切换品牌
const handleChangeBrand = () => {
setBrand();
};
// 初始化品牌
const initBrand = () => {
brand.value = localStorage.getItem('brand') || 'default';
setBrand();
};
/**
* 切换主题
* 1. 设置主题
*/
const handleChangeTheme = () => {
setTheme();
};
// 设置主题
/**
* 设置主题
* 1. 设置当前主题
* 2. 设置主题到本地存储
* 3. 设置主题到根元素
*/
const setTheme = () => {
currentTheme.value = theme.value === 'system' ? getSystemTheme() : theme.value;
localStorage.setItem('theme', theme.value);
const root = document.documentElement;
root.dataset.theme = currentTheme.value;
};
/**
* 监听主题变化
* @param {Event} e 事件
*/
const listenerTheme = (e) => {
console.log(e.matches);
console.log(e);
console.log('theme.value', theme.value);
if(theme.value === 'system') {
currentTheme.value = e.matches ? 'dark' : 'light';
setTheme();
}
};
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
/**
* 初始化主题
* 1. 设置主题
* 2. 监听主题变化
*/
const initTheme = () => {
console.log('initTheme:theme.value', theme.value);
setTheme();
mediaQuery.addEventListener('change', listenerTheme)
};
onMounted(() => {
initTheme();
initBrand();
});
onUnmounted(() => {
mediaQuery.removeEventListener('change', listenerTheme)
});
</script>
<style scoped>
.test_theme {
color: var(--sap-text);
background-color: var(--sap-bg);
padding: 10px;
height: 100px;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
}
</style>less
:root {
--sap-bg: #fff;
--sap-text: #222;
}
:root[data-theme="dark"] {
--sap-bg: #121212;
--sap-text: #fff;
}
:root[data-brand="blue"] {
--sap-bg: #007bff;
--sap-text: #fff;
}
:root[data-brand="green"] {
--sap-bg: #28a745;
--sap-text: #fff;
}
:root[data-brand="yellow"] {
--sap-bg: #ffc107;
--sap-text: #fff;
}
:root[data-brand="red"][data-theme="light"] {
--sap-bg: #dc3545;
--sap-text: #fff;
}
:root[data-brand="red"][data-theme="dark"] {
--sap-bg: #ff0000;
--sap-text: #000000;
}