流行的前端主题切换方案
我们在浏览一些网站时,经常会看到网站具有主题切换的功能,最简单的比如很多网站都提供了护眼模式,说白了也就是有黑色主题和白色主题可以让用户选择,比如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变量值即可,不存在优先级问题
- 新增或者修改主题灵活方便
缺点:
- 兼容性问题
- 首屏加载时会牺牲一点点的时间加载样式资源(可以忽略不计)
参考文档:
#前端##前端架构师##前端开发##网站主题##前端主题颜色#