流行的前端主题切换方案
我们在浏览一些网站时,经常会看到网站具有主题切换的功能,最简单的比如很多网站都提供了护眼模式,说白了也就是有黑色主题和白色主题可以让用户选择,比如Vue3、React官网
还有一些ToC的网站可能会有除了黑白主题之外的更高级的主题需求,比如要支持红色、蓝色、粉色的主题。甚至有一些产品经理提的需求更变态,要支持用户自定义主题色,直接把一个调色板丢给用户,让用户自己选自己喜欢的颜色,页面上所有得主题色都要随着用户自己选择的主题色做更改
。
上面的这三种主题色的需求在实现上正好是由易到难,那么我们改如何实现呢?下面我们就一起来看一下目前流行的几种前端主题方案。
方案1:CSS Filter属性
CSS filter 属性用于将视觉效果应用于元素,比如模糊、亮度、对比度、饱和度、色调、灰度、反转等。这个属性非常强大,可以用来创建各种视觉效果,常用于图片、视频和其他HTML元素。
我们一起来看下使用filter快速实现的暗黑主题的效果
html {
filter: invert(1) hue-rotate(180deg);
}
怎么样?一行代码实现快速暗黑主题。你先别管页面上的图片怎么不太对劲,你先说快不快
关于图片页面上图片失真的原因,是因为我们在html上添加的这个属性,直接把图片的颜色也给反转了,为了解决这个问题,我们在给页面上的所有图片颜色再反转一次
html[theme='dark-mode'] img{
filter: invert(1) hue-rotate(180deg);
}
现在颜色是不是正常了?
当一些公共事件发生时,我们利用filter快速的将网站置灰
html{
filter:grayscale(100%);
}
优点:
- 临时需求我们可以快速上线,代码轻量,功能丰富
缺点:
- 对于C端的产品来说,这个方案并不是完美的, 对于一个网页各个部分的颜色如何过渡,如何设计,一个filter效果往往并不能使设计师满意
方案2:Link标签动态引入
其做法就是提提前构建好几套css主题样式的文件,在用户切换不同的主题时,动态创建link标签加载到head标签中,或者是动态改变link的href(还记不记得href和src属性有什么区别?)属性。
优点:
- 可以实现按需加载,提高了首屏加载时的性能
缺点:
- 动态加载样式文件,如果文件过大网络情况不佳的情况下可能会有加载延迟,导致样式切换不流畅
- 如果主题样式表内定义不当,会有优先级问题
- 各个主题样式是写死的,后续针对某一主题样式表修改或者新增主题也很麻烦
- 支持有限的主题样式,不支持高度的用户自定义主题
方案3:提前引入所有得主题样式,做类名切换
这种方案跟第一种比较类似,主要是解决了动态加载css文件时间不确定的问题,需要在第一加载时将所有不同主题的css样式文件引入,在切换主题时,将指定的根元素类名更换,相当于直接做了样式覆盖,在该类名下各个样式统一更换了。其基本方法如下:
/* day样式主题 */
body.white .box {
color: #f60;
background: #fff;
}
/* dark样式主题 */
body.dark .box {
color: #ddd;
background: #334;
}
.box {
width: 200px;
height: 200px;
}
效果如下:
优点:
- 不用重新加载样式文件,在样式切换的时候不会有卡顿
缺点:
- 首屏加载时会牺牲一些时间加载样式资源
- 如果主题样式定义不当,也会有优先级问题问题
- 各个主题的样式是写死的,后续针对某一主题的样式表修改或者新增主题也很麻烦
方案4:CSS变量+类名切换
Vue3和React官网中就是采用了这个方案,这个也是目前比较流行的方案。答题思路跟方案2相似,依然是提前将样式文件载入,切换的时候将指定的根元素类名更换.
这里相对比较灵活的是,默认在根作用域下定义好CSS变量,只需要在不同的主题下更改CSS变量的取值即可。
/* 定义根作用域下的变量 */
:root {
--theme-color: #333;
--theme-background: #eee;
}
/* 更改dark类名下变量的取值 */
.dark{
--theme-color: #eee;
--theme-background: #333;
}
/* 更改pink类名下变量的取值 */
.pink{
--theme-color: #fff;
--theme-background: pink;
}
.box {
transition: all .2s;
width: 100px;
height: 100px;
border: 1px solid #000;
/* 使用变量 */
color: var(--theme-color);
background: var(--theme-background);
}
优点:
- 不用重新加载样式文件,样式切换没有卡顿
- 在需要切换主题的地方利用var()绑定变量即可,不存在优先级问题
- 新增或者修改主题方便灵活,仅需要新增或者修改CSS变量即可,在Var()绑定样式变量的地方就会自动更换
缺点:
- 需要考虑浏览器对于CSS Variable的兼容性
- 首屏加载时会牺牲一点点时间加载样式资源
方案5 SCSS+mixin+类名切换、
主要是运用scss的混合 + CSS类名切换,其原理主要是将使用到mixin混合的地方编译为固定的css以后,再通过类名切换去做样式覆盖
/* 背景颜色规范(主要) */ $background-color-theme: #d43c33;//背景主题颜色默认 $background-color-theme1: #42b983;//背景主题颜色1 $background-color-theme2: #333;//背景主题颜色2
@import './variable.scss'
@mixin bg_color(){
background: $background-color-theme;
[data-theme=theme1] & {
background: $background-color-theme1;
}
[data-theme=theme2] & {
background: $background-color-theme2;
}
}
<template>
<div class="header" @click="changeTheme">
<div class="header-left">
<slot name="left">左边</slot>
</div>
<slot name="center" class="">中间</slot>
<div class="header-right">
<slot name="right">右边</slot>
</div>
</div>
</template>
<script>
export default {
name: 'Header',
methods: {
changeTheme () {
document.documentElement.setAttribute('data-theme', 'theme1')
}
}
}
</script>
<style scoped lang="scss">
@import "../assets/css/variable";
@import "../assets/css/mixin";
.header{
width: 100%;
height: 100px;
font-size: $font_medium;
@include bg_color();
}
</style>
表现效果如下
这种方案与方案2类似,只是在定义主题时由SCSS直接定义,更加灵活
优点:
- 不需要重新加载样式文件,样式切换时不会有卡顿
- 需要在切换主题的地方利用mixin混合绑定变量即可,不存在优先级问题
- 新增或者修改主题方便灵活,仅需要新增或者修改SCSS变量即可,经过编译后会将所有得主题全部编译出来
缺点:
- 首屏加载时会牺牲一些时间加载样式资源
方案6 CSS变量+动态setProperty
这个方案相对于前几种更加灵活,适用于根据由用户根据颜色面板自行设定各种颜色主题。这种方式主题的自定义行较强。
:root {
--theme-color: #333;
--theme-background: #eee;
--theme-header-color:#ff0;
--theme-menu-colot:#990;
}
定义一个工具类方法,用于修改指定的CSS变量
export const setCssVar = (prop: string, val: any, dom = document.documentElement) => {
dom.style.setProperty(prop, val)
}
主题改变的时候调用这个方法就可以
setCssVar('--theme-color', color)
表现效果如下
优点:
- 不用重新加载样式文件,切换的时候不会有卡顿
- 需要切换主题的地方只需要在:root上动态更改CSS变量值即可,不存在优先级问题
- 新增或者修改主题灵活方便
缺点:
- 兼容性问题
- 首屏加载时会牺牲一点点的时间加载样式资源(可以忽略不计)
方案7 CSS变量+动态setProperty+Color palettes calculator
这个目前是我能想到的前端主题定义的终极解决方案,他允许用户从调色板中任意取色,然后利用调色板,生成该色系深浅不一的各个颜色,用在网站的不同位置,使网站的颜色能通选择的颜色一起变化。
import { generate } from '@ant-design/colors';
// Generate dark color palettes by a given color
const colors = generate('#1890ff');
console.log(colors); // ['#111d2c', '#112a45', '#15395b', '#164c7e', '#1765ad', '#177ddc', '#3c9ae8', '#65b7f3', '#8dcff8', '#b7e3fa']
当我们获取到基于用户设置的色系的所有色值之后,我们就可以利用这些色值,改变页面的不同部分的颜色,使页面看起来更加自然美观。
优点:
- 支持用户自定义任意颜色的主题
- 不用重新加载样式文件,切换的时候不会有卡顿
- 需要切换主题的地方只需要在:root上动态更改CSS变量值即可,不存在优先级问题
- 新增或者修改主题灵活方便
缺点:
- 兼容性问题
- 首屏加载时会牺牲一点点的时间加载样式资源(可以忽略不计)
参考文档:
#前端##前端架构师##前端开发##网站主题##前端主题颜色#
查看5道真题和解析