antd4设置主题色

1,402 阅读2分钟

实现原理

  1. 自定义属性(__*):css 变量

    1. 通过 var 函数在全文档范围内复用的
    2. 定义在根伪类:root 下,这样就可以在 HTML 文档的任何地方访问到它
      • 注意点: :root 这个 CSS 伪类匹配文档树的根元素。对于 HTML 来说,:root 表示 元素,除了优先级更高之外,与 html 选择器相同。
      • 注意点:var 变量方式 ie 不支持;css-vars-ponyfill 插件 ,使页面展示一个默认色
  2. 步骤

     按antd官方文档,要动态切换主题不能引用@import '~antd/dist/antd.less';而需要引用@import '~antd/dist/antd.variable.min.css';
     在src/global.less下引入@import '~antd/dist/antd.variable.min.css'; 如果没有global.less自己创建一个引入@import '~antd/dist/antd.variable.min.css';
    
     引入完毕后,配置动态主题色Config。(此处我使用useModel作为状态管理,有用Redux的也可以换成Redux)
     const Layout: React.FC = (props) => {
       const { theme } = useModel('tabMenu');
    
       ConfigProvider.config({
         theme: {
           primaryColor: theme,
         },
       });
       return (
         <ConfigProvider locale={locale}>
           <IndexPage {...props} />
         </ConfigProvider>
       );
     };
    
     配置好后,切换主题色。改变状态参数theme即可。
    
     例如:
     const { theme, dispatch } = useModel('tabMenu');
       return (
         <div className="main">
           <ColorRGBPicker
             value={theme}
             onChange={(e) => {
               dispatch({ type: 'CHANGE_THEME', payload: { theme: e } });
             }}
           />
         </div>
     );
    
    切换主题一共就这三处地方,极为简单。
    
  3. ConfigProvider.config() —— antd 提供的

     ConfigProvider.config = setGlobalConfig;
     var setGlobalConfig = function setGlobalConfig(_ref) {
     var prefixCls = _ref.prefixCls,
       iconPrefixCls = _ref.iconPrefixCls,
       theme = _ref.theme;
    
     if (prefixCls !== undefined) {
       globalPrefixCls = prefixCls;
     }
    
     if (iconPrefixCls !== undefined) {
       globalIconPrefixCls = iconPrefixCls;
     }
    
     if (theme) {
       (0, \_cssVariables.registerTheme)(getGlobalPrefixCls(), theme); // 更改主题色
     }
    
     /**************antd 内部实现方式******************/
     /**
      * 1. 传入主题色
      * 2. 生成主题色相关的十个色阶
      * 3. 调用 rc-util 下 updateCss 生成的变量放到 :root 根伪类下面
      */
     import { updateCSS } from 'rc-util/lib/Dom/dynamicCSS';
     import { TinyColor } from '@ctrl/tinycolor';
     import { generate } from '@ant-design/colors';
    
     function setThemeColor({ themeColor, varName }) {
       const variables = {};
    
       // ================ Primary Color ================
       if (themeColor) {
         // 转成 TinyColor 色值格式
         const primaryColor = new TinyColor(themeColor);
         // 10个不同阶梯色值
         const colorPalettes = generate(primaryColor.toRgbString());
         // Legacy - We should use semantic naming standard
         variables[`${varName}`] = themeColor;
         colorPalettes.forEach((color, index) => {
           variables[`${varName}-${index + 1}`] = color;
         });
       }
    
       // Convert to css variables
       // cssList ['--theme-color: xxx','--theme-color-1: xxx',...,'--theme-color-10: xxx' ]
       const cssList = Object.keys(variables).map(
         key => `--${key}: ${variables[key]};`,
       );
    
       // updateCSS 更新业务代码中的主题色: 创建 style 标签,值为cssList,并作为最后一个子元素插在head标签下
       updateCSS(
         '\n  :root {\n    '.concat(cssList.join('\n'), '\n  }\n  '),
         '-mkui-fd-'.concat(Date.now(), '-').concat('-dynamic-theme'),
       );
     }
    

参考资料

  1. antd 动态主题
  2. 基于 HSB 模型构建色彩体系
  3. Ant Design 色板生成算法演进之路