前端实现黑夜模式/换肤最完整解决方案

1,250 阅读3分钟

⏳ 前言

入职了新公司,负责的项目页面UI风格偏亮,对于日常习惯黑夜模式的我来说视觉上有点不习惯,所以萌生了做黑夜模式的想法.另一方面夜间模式已经逐渐成为前端项目不可缺少的一部分.本着学习的想法,一步一步成功在项目中应用.这里分享一下过程以及心得.

需求分析

实现主题配色的通常需求:

  • 在不刷新页面条件下动态切换主题
  • 兼容一些需要JS驱动的插件,比如:echart等

代码实现

将颜色变量同时绑定到 :root 和某个公共元素容器上,通过切换容器的属性来切换变量对应的色值

// css
// 正常展示的样式
:root {
  --theme-color: #416eff; // 按钮颜色以及单选多选等主体色
  --theme-text-color: #373a44; // 文字颜色
  --theme-bk: #f4f9fe; // 主题背景颜色
  --theme-icon: inherit; // 大部分icon颜色
  --theme-arrow-icon: #373a44; // sidebar三角箭头icon
  --theme-input-arrow-icon: #bfbfbf; // 下拉和select 三角箭头icon
  --theme-page-header-1: rgba(0, 0, 0, 0.45); // 头部导航文字1
  --theme-page-header-2: #373a44; // 头部导航文字2
  --theme-input-bk: #ffffff; // input的背景色
}
// 黑夜模式下展示的样式
html[darkreader-scheme='one'] {
  --theme-color: #416eff;
  --theme-text-color: #ffffff;
  --theme-bk: #1a1c31;
  --theme-icon: #ffffff; // icon颜色
  --theme-arrow-icon: rgba(255, 255, 255, 0.5); // 三角箭头icon
  --theme-input-arrow-icon: rgba(255, 255, 255, 0.5); // 下拉和select 三角箭头icon
  --theme-page-header-1: rgba(255, 255, 255, 0.5); // 头部导航文字1
  --theme-page-header-2: #ffffff; // 头部导航文字2
  --theme-input-bk: none; // input的背景色
}

通过var() 来使用变量

// tab选项卡
.ant-tabs {
  .ant-tabs-nav {
    &:before {
      background-color: var(--theme-pro-table-bk);
    }
    .ant-tabs-nav-wrap {
      .ant-tabs-nav-list {
        .ant-tabs-tab {
          .ant-tabs-tab-btn {
            color: var(--theme-text-color);
          }
        }
        .ant-tabs-tab-active {
          background-color: var(--theme-pro-table-bk);

          .ant-tabs-tab-btn {
            color: var(--theme-color);
          }
        }
        .ant-tabs-ink-bar {
          background-color: var(--theme-color);
        }
      }
    }
  }
}

通过JS控制html标签上的自定义属性来动态切换样式变量

 const handleSwitchChange = (flag: Boolean) => {
    if (flag) {
      localStorage.setItem('nightFlag', 'one')
      document.querySelectorAll('html')[0].setAttribute('darkreader-scheme', 'one')
    } else {
      localStorage.removeItem('nightFlag')
      document.querySelectorAll('html')[0].setAttribute('darkreader-scheme', 'none')
    }
  }

如果需要驱动一些插件 比如echart的一些颜色属性,需要重新init,那么可以通过状态管理来存储状态,页面监听属性 来重新初始化echart组件

我的实现步骤思路

我的这个项目是管理端,用的antd组件.我的做法是:首先定义整体的背景色.如默认白色,切换后黑色.实现了这个简单的东西后,再去抽离button,input,table等等常用的组件.当整体有一个大方向后再去改一些比较冷门不常用的组件和一些单独的无法应用公共样式的页面.这个是慢工细活,总会有一下子照顾不到的.需要一点一点看每个页面有没有遗漏的地方.

关于换肤的实现

其实同理,也一样是按照上边那样来操作.有点细节,换肤的颜色要合理的运用透明度颜色和一些默认样式为none

动画1.gif

代码:

// 基本样式
:root {
  --theme-bk: #f4f9fe; // 主题背景颜色
  --theme-pro-table-bk: #ffffff; // 页面主体背景色
  --theme-input-bk: #ffffff; // input的背景色
}
// 黑夜模式
html[darkreader-scheme='one'] {
  --theme-bk: #1a1c31;
  --theme-pro-table-bk: #282a3e;
  --theme-input-bk: none; // input的背景色
}
// 换肤样式 
html[darkreader-scheme='two'] {
  --theme-bk: url('~@/assets/images/background/bk.jpg') 0 / cover fixed;
  --theme-pro-table-bk: rgba(42, 44, 44, 0.8);
  --theme-input-bk: none; 
}

结语

按照这个思路开发 基本就完成了.如果有问题,欢迎评论区提问