antd动态主题的原理和实现

what

动态主题是指:当用户主动触发某种交互的时候,页面的颜色内容发生变化;

动态主题最常见的场景就是 黑暗模式

动态主题的实现方式和原理

动态主题的实现方式本质上是和 css 技术栈有关系的:

  1. less - less.modifyVars
  2. css - var css
  3. css in js - js change css

less.modifyVars

这种方式是在页面引入 less.js 以及 less 文件,然后通过修改 less 变量的方式来达到动态换肤的效果

demo

关键代码:

html 复制代码
<script>
  const btn = document.querySelector('button');
  btn.addEventListener('click', () => {
    less.modifyVars({
      '@primary-color': '#cf1322',
    });
  });
</script>

运行效果:

var css

这种方式是通过在不同的 css 选择器下面定义 css 变量值来达到动态换肤的效果

demo

关键代码:

html 复制代码
<style>
  :root {
    --bg: #fff;
    --font-color: red;
  }

  :root[data-dark-theme='true'] {
    --bg: blue;
    --font-color: black;
  }

  .container {
    width: 100vw;
    height: 100vh;
    background-color: var(--bg);
    color: var(--font-color);
  }
</style>

运行效果:

css in js - js change css

css in js 是一种 css 解决方案,它使用 js 将 css 规则插入到页面中

这里以 emotion 为例,我们可以直接修改 theme 对应的主题变量

demo

关键代码:

jsx 复制代码
const Layout = () => {
  const [theme, setTheme] = useState({
    colors: {
      primary: 'red',
    },
  });

  const btnFn = () => {
    setTheme({
      colors: {
        primary: 'blue',
      },
    });
  };

  return (
    <ThemeProvider theme={theme}>
      <div className={styles['layout-container']}>
        <button onClick={btnFn}>change theme</button>
        <nav>
          {routesList.map((ele) => (
            <div
              className={styles['btn']}
              key={ele}
            >
              <Link to={ele}>{ele}</Link>
            </div>
          ))}
        </nav>

        <hr />

        <Outlet />
      </div>
    </ThemeProvider>
  );
};

运行效果:

antd@4 动态主题解决方案

antd-theme-generator

antd-theme-generator 是一个将 antd 中所有的 less 变量提取成一个单独文件的插件,可以参见下面的资料:

如何在 umi 系项目中实现动态换肤

Ant Design Runtime Theme Update #10007

解决方案的 demo:demo

关键代码:

js 复制代码
const path = require('path');
const { generateTheme } = require('antd-theme-generator');

const options = {
  // 这是 node_modules 中 antd 目录的路径
  antDir: path.join(__dirname, './node_modules/antd'),
  // 指向包含 .less 文件的自定义样式目录的路径/路径
  stylesDir: path.join(__dirname, './src'), // all files with .less extension will be processed
  // 主题相关变量文件的路径
  varFile: path.join(__dirname, './src/styles/vars.less'), // default path is Ant Design default.less file
  // List of variables that you want to dynamically change
  themeVariables: [
    '@primary-color',
    '@link-color',
    '@success-color',
    '@warning-color',
    '@error-color',
    // '@font-size-base',
    '@heading-color',
    '@text-color',
    '@text-color-secondary',
    '@disabled-color',
    '@border-radius-base',
    '@border-color-base',
    '@box-shadow-base',
  ],
  // 生成的较少内容将写入指定的文件路径,否则不会写入。不过,你可以使用返回的输出,并写入任何你想要的文件中
  outputFilePath: path.join(__dirname, './public/color.less'), // if provided, file will be created with generated less/styles
  // 该数组用于提供与颜色值相匹配的 regex,大多数情况下您并不需要它
  customColorRegexArray: [/^fade\(.*\)$/], // An array of regex codes to match your custom color variable values so that code can identify that it's a valid color. Make sure your regex does not adds false positives.
};

generateTheme(options)
  .then((less) => {
    console.log('Theme generated successfully');
  })
  .catch((error) => {
    console.log('Error', error);
  });

运行效果:

antd ConfigProvider

ConfigProvider - 动态主题

ConfigProvider 方案的本质是将 primaryColor 这些公共变量抽象成为 css 变量,然后通过调用 ConfigProvider.config 这个 API 去修改这些变量的值(即对应的是 var css 方案)

方案 demo

关键代码:

jsx 复制代码
const btnCallback = () => {
    // 替换主题
    ConfigProvider.config({
      prefixCls: 'custom',
      theme: {
        primaryColor: '#002766', // 全局主色
        infoColor: '#780650', // info 颜色,Alert 组件的 info 类型的 bg color
        successColor: '#092b00', // 成功色
        processingColor: '#1890ff', // 这个颜色暂时不知道具有作用在哪些组件里面,在 ConfigProvider 组件中搜索也没有发现使用的记录
        warningColor: '#613400', // 警告色
        errorColor: '#5c0011', // 错误色
      },
    });
 };

运行效果:

方案比较

方案 优点 缺点
antd-theme-generator 1. 支持自定义的范围大,至少支持 12 个 less 变量的自定义 1. 有些变量可能不支持,比如 @white 2. 对于不antd@4.17 之前的版本兼容性可能比较好,之后的版本边界需要自己确认一下,可能会存在某些变量不能定义的情况 3. 配置相对而言比较复杂
antd ConfigProvider 1. 配置简单 1. 官网文档上说这个是一个实验方案 2. 支持的变量比较少,只支持 6 个变量

ConfigProvider Theme 配置的定义:

ts 复制代码
export interface Theme {
  primaryColor?: string;
  infoColor?: string;
  successColor?: string;
  processingColor?: string;
  errorColor?: string;
  warningColor?: string;
}

方案建议

  1. 如果自定义主题的场景比较简单,能够使用 ConfigProvider 的 6 个变量实现的话,推荐使用 ConfigProvider 方案
  2. 如果 ConfigProvider 不能满足自定义的需求,才推荐使用 antd-theme-generator 方案

ant@5 动态主题解决方案

antd@5 是基于 css in js 的,因此技术上直接支持动态主题,加上 antd@5 本身对于动态主题支持也很完成,因此这部分建议直接看官方文档即可 ant.design/docs/react/...

QA

antd 中定义的 less 变量有哪些,如果判断这些变量的影响范围?

参考链接 - 定制主题

根据文档中的描述,antd 中定义的 less 变量有:

less 复制代码
@primary-color: #1890ff; // 全局主色
@link-color: #1890ff; // 链接色
@success-color: #52c41a; // 成功色
@warning-color: #faad14; // 警告色
@error-color: #f5222d; // 错误色
@font-size-base: 14px; // 主字号
@heading-color: rgba(0, 0, 0, 0.85); // 标题色
@text-color: rgba(0, 0, 0, 0.65); // 主文本色
@text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色
@disabled-color: rgba(0, 0, 0, 0.25); // 失效色
@border-radius-base: 2px; // 组件/浮层圆角
@border-color-base: #d9d9d9; // 边框色
@box-shadow-base: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08),
  0 9px 28px 8px rgba(0, 0, 0, 0.05); // 浮层阴影

如果需要判断这些变量的影响范围,我们根据themes/default.less判断

参考链接

  1. 如何在 umi 系项目中实现动态换肤
  2. using-less-in-the-browser
  3. antd default.less
  4. 实现 antd 动态主题的两种方式
  5. antd 定制主题
  6. github antd-theme-generator
  7. emotion - 官方文档
  8. emotion - gitub
  9. antd@5 文档
相关推荐
mldong6 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排7 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60618 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角9 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!9 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰9 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi10 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen11 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马12 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu12 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss