Skip to content

多品牌、亮色暗色模式

思路

亮色、暗黑模式:

使用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;
}