useCssVar
实现原理是通过修改css变量的值来自定义主题色,可以单独修改某个dom下的css变量而不影响全局css变量的使用,这就使得可定制主题区域变得非常灵活。具体使用方式请查看官网地址
最终效果图如下:
主要实现代码:
import { CssVarUnion, ThemeUnion } from './type';
import { Themes } from './theme';
import { useLocalCache } from '@/hooks/useLocalCache';
const { setCache } = useLocalCache();
export function useSwitchTheme(
el: HTMLElement | Ref<any>,
type: Ref<ThemeUnion> = ref('defaultTheme')
) {
const colors: Record<CssVarUnion | string, Ref<any>> = {};
Object.keys(Themes.defaultTheme).forEach((item) => {
colors[item] = useCssVar(item, el);
});
const switchColor = () => {
Object.keys(Themes[type.value]).forEach((item) => {
colors[item].value = Themes[type.value][item as CssVarUnion];
});
setCache('theme', type.value);
};
return {
switchColor,
};
}
// type.ts
// a | b | c ---> { a:V; b:V; c:V }
type UnionToObj<K extends string, V = string> = {
[P in keyof { K: K } as `${K}`]: V;
};
// css变量
export type CssVarUnion = '--sy-primary-title-color' | '--sy-primary-bg-color';
export type TCssVar = Partial<UnionToObj<CssVarUnion>>;
// 主题
export type ThemeUnion = 'defaultTheme' | 'customTheme';
export type TTheme = UnionToObj<ThemeUnion, TCssVar>;
// theme.ts
import { TTheme } from './type';
export const Themes: TTheme = {
defaultTheme: {
'--sy-primary-bg-color': '#fff',
'--sy-primary-title-color': '#000',
},
customTheme: {
'--sy-primary-bg-color': '#000',
'--sy-primary-title-color': '#fff',
},
};
// theme.scss(需要将主题色样式文件在main.ts中引入)
// 可以将css变量写在具体的dom下,例如:body, #app等...
:root {
--sy-primary-bg-color: #fff;
--sy-primary-title-color: #000;
}
代码中用到的useLocalCache
可以参考之前的文章vueuse-useLocalStorage在项目中的使用
// 在useLocalCache增加theme
const defCache: ILocalCache = {
...,
theme: 'defaultTheme',
};
// 获取cache
function getCache(key: 'theme'): ThemeUnion;
...
// 设置cache
function setCache(key: 'theme', value: ThemeUnion): void;
...
使用:
<div class="theme-box" ref="el">
<div class="text">主题测试区域</div>
</div>
.theme-box {
width: 180px;
height: 40px;
border: 1px solid #ccc;
background-color: var(--sy-primary-bg-color);
color: var(--sy-primary-title-color);
}
const el = ref(null);
const activeTheme = ref<ThemeUnion>(getCache('theme'));
const { switchColor } = useSwitchTheme(el, activeTheme);
nextTick(() => {
switchColor();
});
除此之外,还可以结合ui框架实现定制主题色,例如element-plus等
2023-7-28更新
更新原因同1.vueuse-useLocalStorage在项目中的使用。旧的类型定义太过分散,且不好维护,不利于阅读。本次更新删除了theme.ts
及type.ts
文件,并将useSwitchTheme
更名为useTheme
。更详细部分请查看 github
// useTheme.ts
import { useLocalCache } from '@/hooks/useLocalCache';
const { setCache } = useLocalCache();
const themes = {
defaultTheme: {
'--sy-primary-bg-color': '#fff',
'--sy-primary-title-color': '#000',
},
customTheme: {
'--sy-primary-bg-color': '#000',
'--sy-primary-title-color': '#fff',
},
};
type Theme = typeof themes;
export type ThemeTypes = keyof Theme;
export type CssVarTypes = keyof Theme['defaultTheme'];
export function useTheme(
el: HTMLElement | Ref<HTMLElement | null | undefined>,
type: Ref<ThemeTypes> = ref('defaultTheme')
) {
const colors: Record<CssVarTypes | string, Ref<any>> = {};
const switchColor = () => {
Object.keys(themes[type.value]).forEach((item) => {
colors[item].value = themes[type.value][item as CssVarTypes];
});
setCache('theme', type.value);
};
onMounted(() => {
Object.keys(themes.defaultTheme).forEach((item) => {
colors[item] = useCssVar(item, el);
});
switchColor();
});
return {
switchColor,
};
}
vue中使用示例
const el = ref<HTMLElement>();
const activeTheme = ref<ThemeTypes>(getCache('theme'));
const { switchColor } = useTheme(el, activeTheme);