现在我们经常可以看到一些网站会有类似暗黑模式/白天模式的主题切换功能,效果也是十分炫酷,在平时的开发场景中也有越来越多这样的需求,尤其是后台管理系统、企业官网、文章、试题项目中更加普遍,下面简单罗列几种解决方案:
1.通过修改link标签来切换样式
其做法就是提前准备好几套CSS主题样式文件,在需要的时候,创建link标签动态加载到head标签中,或者是动态改变link标签的href属性。
<link rel="stylesheet " href="./dark.css" ty="darkTheme" >
<link rel="stylesheet" href="./light.css" ty="lightTheme">
优点:实现了按需加载,提高了首屏加载时的性能
缺点:动态加载样式文件,如果文件过大网络情况不佳的情况下可能会有加载延迟,导致样式切换不流畅,如果主题样式表内定义不当,会有优先级问题,各个主题样式是写死的,后续针对某一主题样式表修改或者新增主题也很麻烦
2.提前引入所有主题样式,做类名切换
在需要切换主题的时候将指定的根元素类名更换,相当于直接做了样式覆盖,在该类名下的各个样式就统一地更换了。其基本方法如下:
/* light亮色主题 */
body.light .box {
color: #f90;
background: #fff;
}
/* dark暗色主题 */
body.dark .box {
color: #eee;
background: #333;
}
跟第一种相比:不用重新加载样式文件,在样式切换时不会有卡顿。但是首屏加载时会牺牲一些时间加载样式资源,各个主题样式是写死的,后续针对某一主题样式表修改或者新增主题也很麻烦
3.CSS变量+类名切换
灵感参考: Vue3.0官网
Vue的官网在切换主题时就非常的丝滑,同时还使用了 color-scheme: dark;
将系统的滚动条设置为了黑色模式,使样式更加统一。
思路方案和第2种差不多,依然是提前将样式文件载入,切换时将指定的根元素类名更换。不过这里相对灵活的是,默认在根作用域下定义好CSS变量,只需要在不同的主题下更改CSS变量对应的取值即可。
:root {
--BG: #14191C;
--TextColor: #FFFFFF;
--TextColorLight: #0095B7;
--eduColor: #0095B7;
--expColor: #FF7F00;
}
/* 更改dark类名下变量的取值 */
.dark{
--theme-color: #eee;
--theme-background: #333;
}
/* 更改pink类名下变量的取值 */
.light{
--theme-color: #000000;
--theme-background: #ffffff;
}
.box {
transition: all .2s;
width: 100px;
height: 100px;
border: 1px solid #000;
/* 使用变量 */
color: var(--theme-color);
background: var(--theme-background);
}
优点:
- 不用重新加载样式文件,在样式切换时不会有卡顿
- 在需要切换主题的地方利用var()绑定变量即可,不存在优先级问题
- 新增或修改主题方便灵活,仅需新增或修改CSS变量即可,在var()绑定样式变量的地方就会自动更换
缺点:其实也没啥缺点,硬要说就是适配IE浏览器或者是首屏加载速度的问题
一般在工作中我都使用第三种,这些都是自己定义的,如果要用户端自己根据拾色板来确定要是的话,可以使用如下方案:
CSS变量+动态setProperty
这种方案主要用于是主题颜色不确定的情况
参考项目: ElementAdmin
主要实现思路如下:
只需在全局中设置好预设的全局CSS变量样式,无需单独为每一个主题类名下重新设定CSS变量值,因为主题是由用户动态决定。
:root {
--theme-color: #333;
--theme-background: #eee;
}
定义一个工具类方法,用于修改指定的CSS变量值,调用的是 CSSStyleDeclaration.setProperty
export const setCssVar = (prop: string, val: any, dom = document.documentElement) => {
dom.style.setProperty(prop, val)
}
在样式发生改变时调用此方法即可
setCssVar('--theme-color', color)
刷掘金看到了评论用滤镜反色效果的 filter:invert(1);
用在掘金上乍一看还凑合,但是自己的项目就不太行了,这个只能当个玩笑,不建议使用!