暗黑模式及主题切换:
项目中使用的组件库为 element-plus, 同时使用tailwind语法构建样式
因此直接使用elment-plus的暗黑以及主题切换
一、element-plus主题切换
原理:通过阅读element源码 目录:/theme-chalk/src/common/var.scss
// elment主题色 --el-color-primary
$color-primary: map.get($colors, 'primary', 'base') !default;
// 混入不同明亮等级样式
@mixin set-color-mix-level(
$type,
$number,
$mode: 'light', // 明亮模式
$mix-color: $color-white
) {
$colors: map.deep-merge(
(
$type: (
'#{$mode}-#{$number}':
mix(
$mix-color,
map.get($colors, $type, 'base'),
math.percentage(math.div($number, 10))
),
),
),
$colors
) !global;
}
我们可以知道与主题色相关的被分为十个等级,并不断变淡。最终得出与主题色相关的变量如下
// $colors.primary.light-i
// --el-color-primary-light-i
// 10% 53a8ff
// 20% 66b1ff
// 30% 79bbff
// 40% 8cc5ff
// 50% a0cfff
// 60% b3d8ff
// 70% c6e2ff
// 80% d9ecff
// 90% ecf5ff
因此我们可以通过js设置这几个css变量达到主题切换的效果
const el = document.documentElement
el.style.setProperty('--el-color-primary', 'red')
从源码解析,element在暗黑模式只用了一种css变量 --el-color-primary-dark-2
// --el-color-primary-dark-2
@each $type in $types {
@include set-color-mix-level($type, 2, 'dark', $color-black);
}
接下来创建调节颜色的工具方法
// tools.ts
/**
* 加深颜色值
* @param color 颜色值字符串
* @param level 加深的程度,限0-1之间
* @returns 返回处理后的颜色值
*/
export function getDarkColor(color: string, level: number) {
const reg = /^#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.round(20.5 * level + rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
/**
* 变浅颜色值
* @param color 颜色值字符串
* @param level 加深的程度,限0-1之间
* @returns 返回处理后的颜色值
*/
export function getLightColor(color: string, level: number) {
const reg = /^#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
const rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.round(255 * level + rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
创建好更改的方法,可以动态的切换主题颜色了
// useTheme.ts
// 修改主题颜色
const changePrimary = (val: string) => {
if (!val) {
val = DEFAULT_PRIMARY;
ElMessage.success(`主题色已重置为 ${DEFAULT_PRIMARY}`);
}
appStore.setThemeConfig({ ...themeConfig.value, primary: val });
document.documentElement.style.setProperty("--el-color-primary", themeConfig.value.primary);
document.documentElement.style.setProperty("--el-color-primary-dark-2", themeConfig.value.isDark ? `${getLightColor(themeConfig.value.primary, 0.2)}` : `${getDarkColor(themeConfig.value.primary, 0.3)}`);
document.documentElement.style.setProperty("--primary", themeConfig.value.primary);
// 颜色加深或变浅
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, themeConfig.value.isDark ? `${getDarkColor(themeConfig.value.primary, i / 10)}` : `${getLightColor(themeConfig.value.primary, i / 10)}`);
document.documentElement.style.setProperty(`--primary-light-${i}`, themeConfig.value.isDark ? `${getDarkColor(themeConfig.value.primary, i / 10)}` : `${getLightColor(themeConfig.value.primary, i / 10)}`);
}
};
添加暗黑模式
引入暗黑模式的样式
// main.ts
import "element-plus/theme-chalk/dark/css-vars.css";
创建切换暗黑模式的方法,就是给body加上类名
// isDark字段控制明暗状态,swichDark根据isDark执行切换
// 切换暗黑模式
const switchDark = () => {
const body = document.documentElement as HTMLElement;
const html = document.querySelector("html") as HTMLElement;
if (themeConfig.value.isDark) {
setDarkTheme();
body.setAttribute("class", "dark");
} else {
setLightTheme();
body.setAttribute("class", "");
}
changePrimary(themeConfig.value.primary);
};
对于背景或者部分组件样式,可以创建scss文件引入覆盖来实现自定义
/* 自定义 element 暗黑模式样式 */
html.dark {
// layout
.el-container {
//左边栏
.el-aside {
background-color: var(--boxbg) !important;
}
.el-main {
background-color: var(--main) !important;
}
}
// menu 菜单的字体图标颜色变化
.el-menu,
.el-sub-menu,
.el-menu-item,
.el-sub-menu__title {
background-color: var(--boxbg) !important;
&:not(.is-active) {
color: #bdbdc0 !important;
}
&.is-active {
color: #ffffff !important;
background-color: #000000 !important;
}
}
.el-menu-item:not(.is-active):hover {
background-color: var(--main) !important;
}
// 全局字体变化
color: #A6ADBA;
}
如果你使用了daisyUI的主题CSS变量,或者tailwind中设置了css变量想根据明暗切换。可以创建一个文件用来改变CSS变量
const light = {
"base-100": "#ffffff",
"base-300": "#E5E6E6",
"base-content": "#1f2937",
"base-200": "#fbfbfb",
"--boxbg": "#ffffff",
"--border": "#D9D9D9",
"--text": "#D9D9D9",
"--main": "#fbfbfb",
};
const dark = {
"base-100": "#2A303C",
"base-200": "#242933",
"base-300": "#20252E",
"base-content": "#A6ADBB",
//s
"--boxbg": "#141414",
"--border": "#4c4c4d",
"--text": "#4c4c4d",
"--main": "#0d0d0d",
};
export const setDarkTheme = () => {
const arr = Object.keys(dark);
arr.forEach((key) => {
document.documentElement.style.setProperty(key, dark[key]);
});
};
export const setLightTheme = () => {
const arr = Object.keys(light);
arr.forEach((key) => {
document.documentElement.style.setProperty(key, light[key]);
});
};
最终的hook文件
import { getLightColor, getDarkColor } from "./tools";
import { DEFAULT_PRIMARY } from "@/utils/globalConfig";
import { BRAND_THEME, BRAND_ADDRESS } from "@/utils/globalConfig";
import { useAppStore } from "@/vendors/store/modules/app";
import { setDarkTheme, setLightTheme } from "./setColor";
export const useTheme = () => {
const appStore = useAppStore();
const themeConfig = computed(() => appStore.themeConfig);
// isDark字段控制明暗状态,swichDark根据isDark执行切换
// 切换暗黑模式
const switchDark = () => {
const body = document.documentElement as HTMLElement;
const html = document.querySelector("html") as HTMLElement;
if (themeConfig.value.isDark) {
setDarkTheme();
body.setAttribute("class", "dark");
} else {
setLightTheme();
body.setAttribute("class", "");
}
changePrimary(themeConfig.value.primary);
};
// 修改主题颜色
const changePrimary = (val: string) => {
if (!val) {
val = DEFAULT_PRIMARY;
ElMessage.success(`主题色已重置为 ${DEFAULT_PRIMARY}`);
}
appStore.setThemeConfig({ ...themeConfig.value, primary: val });
document.documentElement.style.setProperty("--el-color-primary", themeConfig.value.primary);
document.documentElement.style.setProperty("--el-color-primary-dark-2", themeConfig.value.isDark ? `${getLightColor(themeConfig.value.primary, 0.2)}` : `${getDarkColor(themeConfig.value.primary, 0.3)}`);
document.documentElement.style.setProperty("--primary", themeConfig.value.primary);
// 颜色加深或变浅
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, themeConfig.value.isDark ? `${getDarkColor(themeConfig.value.primary, i / 10)}` : `${getLightColor(themeConfig.value.primary, i / 10)}`);
document.documentElement.style.setProperty(`--primary-light-${i}`, themeConfig.value.isDark ? `${getDarkColor(themeConfig.value.primary, i / 10)}` : `${getLightColor(themeConfig.value.primary, i / 10)}`);
}
};
// 初始化 theme 配置
const initTheme = () => {
useAppStore().themeConfig.isDark = localStorage.getItem("syncTheme") === "dark";
switchDark();
const themeColor = getTheme();
changePrimary(themeColor);
};
// 获取主题
const getTheme = () => {
const currentHost = window.location.host;
const PROJECT_NAME = BRAND_ADDRESS[currentHost] || "Hamedal";
return BRAND_THEME[PROJECT_NAME];
};
return { changePrimary, initTheme, switchDark };
};