项目动态换肤

232 阅读2分钟

前言✨

根据项目需求(其实也是个人的需求),做一个可以动态更换主题色的功能点。项目用的是umi 3.5 + antd 4.21.3

一、编译多份预设主题

编译构建的时候就有多份预设主题,在运行时只做样式切换,但是需要写多份样式,成本较高,而且css代码量也较多,最大的问题是如何直接赋值给antd组件,和设置阶梯色,遂pass

二、CSS变量

CSS变量是CSS3标准的新功能,本博客用户端用的就是这种方案,使用less @mixin 结合css变量,通过切换<html>标签属性data-color-mode来修改样式选择。这是非常不错的方案,性能好,除了IE其他浏览器兼容都没问题。但是在使用第三方组件如antd下就不那么好用了,因为antd变量什么的都自成一派。最大的问题是如何直接赋值给antd组件,和设置阶梯色,遂pass

// theme.scss
@mixin colors-light {
  //品牌色
  --color-text-brand: #087ea4;
}

@mixin colors-dark {
  //品牌色
  --color-text-brand: #149eca;
}

:root,
[data-color-mode="light"] {
  @include colors-light;
}

[data-color-mode="dark"] {
  @include colors-dark;
}
// theme.ts
/**
 * 页面刷新时从localStorage加载主题
 */
const initTheme = () => {
  const mode = localStorage.getItem("data-color-mode");
  mode && document.querySelector("html")?.setAttribute("data-color-mode", mode);
};

/**
 * 设置主题
 */
const setTheme = (theme: string) => {
  document.querySelector("html")?.setAttribute("data-color-mode", theme);
  localStorage.setItem("data-color-mode", theme);
};

/**
 * 获取主题
 */
const getTheme = () => {
  const mode = localStorage.getItem("data-color-mode");
  return mode || 'light'
};
export { initTheme, setTheme, getTheme };

使用如下:

import React, { useEffect, useState } from 'react';
import type { FC } from 'react';
import { setTheme, getTheme } from '@/utils/theme';

const ThemeIcon: FC = () => {
  const [active, setActive] = useState('light');
  useEffect(() => {
    setActive(getTheme())
  }, [])
  const handleChange = (val: string) => {
    setActive(val === 'light' ? 'dark' : 'light');
    setTheme(val);
  };
  useEffect(() => {
    setTheme(active);
  }, [active]);
  return (
    <>
    </>
  );
};

export default ThemeIcon;

div {
    color: var(--color-text-brand);
}

三、使用Antd提供的CSS变量

ant.design/docs/react/…

这和方法二师出同源,唯一区别是方法二采用自己维护的一套样式变量,这种是直接采用antd内置的变量。

  • 优点:自带变量名和阶梯色,只需要一个主色调即可完成组件色彩搭配。
  • 缺点:不支持IE,该功能在 antd@4.17.0-alpha.0 版本起支持

1.引入 antd.variable.min.css

umi根目录 global.less文件下

-- @import '~antd/dist/antd.min.css';
++ @import '~antd/dist/antd.variable.min.css';

2.关闭antd按需加载

umi会自动对antd进行按需引入,在.umirc.ts关闭

antd: {
   disableBabelPluginImport: true
}

3.使用ConfigProvider配置

在umi入口文件配置即可

import { ConfigProvider } from 'antd';
  ConfigProvider.config({
    theme: { primaryColor: '25b864' } // 做切换的话primaryColor改成变量即可
  });

4.使用antd内置变量名

以上操作做完antd本身的组件就已经支持动态切换主题色了,但是自己写的组件、文本还不行,所以要使用和antd组件一样的css变量名。如果不清楚某些组件叫什么名字,直接F12自提,或者翻阅源码。

.view {
  color: var(--ant-primary-color);
}

效果如下:

ezgif.com-gif-maker.gif