流行的前端主题切换方案

我们在浏览一些网站时,经常会看到网站具有主题切换的功能,最简单的比如很多网站都提供了护眼模式,说白了也就是有黑色主题和白色主题可以让用户选择,比如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变量值即可,不存在优先级问题
  • 新增或者修改主题灵活方便

缺点:

  • 兼容性问题
  • 首屏加载时会牺牲一点点的时间加载样式资源(可以忽略不计)

参考文档:

#前端##前端架构师##前端开发##网站主题##前端主题颜色#
全部评论

相关推荐

Java抽象带篮子:可以看看我的置顶帖子,里面写了技术栈怎么描述
点赞 评论 收藏
分享
评论
点赞
6
分享

创作者周榜

更多
牛客网
牛客企业服务