React + Antd 动态换主题实现

React + Antd 动态换主题实现

搭建React工程

首先按照React官网的创建工程的流程使用create-react-app脚手架创建一个React工程,然后按照antd官网推荐的craco(一个对 create-react-app 进行自定义配置的社区解决方案)来进行高级配置(这里按照antd官网教程一步一步完成即可)

自定义主题

安装 craco-less 并修改 craco.config.js 文件如下。

注意:modifyVars必须配置为一个空对象,如果传值会导致动态更换主题失败

const CracoLessPlugin = require('craco-less');

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: { },
            //modifyVars: { '@primary-color': '#1DA57A' },
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};
安装webpack-theme-color-replacer插件
yarn add webpack-theme-color-replacer@1.3.22

由于craco配置的时候已经引入了craco-less插件,因此这里不需要再继续安装less以及less-loader

配置插件

在src目录下创建utils文件夹,以及plugin.config.js以及utils.js两个文件

安装完成后对插件进行配置生成,然后暴露修改主题的方法updateTheme

plugin.config.js是配置webpack-theme-color-replacer

plugin.config.js

const ThemeColorReplacer = require("webpack-theme-color-replacer");
const generate = require("@ant-design/colors").generate;
// generate方法和ant-design-vue获取方式的区别
// react: require("@ant-design/colors").generate;
// vue: require('@ant-design/colors/lib/generate').default

const getAntdSerials = (color) => {
  // 淡化(即less的tint)
  const lightens = new Array(9).fill().map((t, i) => {
    return ThemeColorReplacer.varyColor.lighten(color, i / 10);
  });
  const colorPalettes = generate(color);
  const rgb = ThemeColorReplacer.varyColor
    .toNum3(color.replace("#", ""))
    .join(",");
  return lightens.concat(colorPalettes).concat(rgb);
};

const themePluginOption = {
  fileName: "css/theme-colors-[contenthash:8].css",
  matchColors: getAntdSerials("#1890ff"), // 主色系列
  // 改变样式选择器,解决样式覆盖问题
  changeSelector(selector) {
    switch (selector) {
      case ".ant-calendar-today .ant-calendar-date":
        return (
          ":not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)" +
          selector
        );
      case ".ant-btn:focus,.ant-btn:hover":
        return ".ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)";
      case ".ant-btn.active,.ant-btn:active":
        return ".ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)";
      case ".ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon":
      case ".ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon":
        return ":not(.ant-steps-item-process)" + selector;
      case ".ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover":
      case ".ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover":
        return ".ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover";
      case ".ant-menu-horizontal > .ant-menu-item-selected > a":
      case ".ant-menu-horizontal>.ant-menu-item-selected>a":
        return ".ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a";
      case ".ant-menu-horizontal > .ant-menu-item > a:hover":
      case ".ant-menu-horizontal>.ant-menu-item>a:hover":
        return ".ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover";
      default:
        return selector;
    }
  },
};

const createThemeColorReplacerPlugin = () =>
  new ThemeColorReplacer(themePluginOption);

module.exports = createThemeColorReplacerPlugin;

plugin.config.js暴露后需要添加为webpack的-plugin

查阅资料后发现,在craco.config.js里面是可以配置webpack的(类似于vue在vue.config.js里面配置webpack)

配置后的craco.config.js

const CracoLessPlugin = require('craco-less');
const createThemeColorReplacerPlugin = require("./src/utils/plugin.config");

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: {  },
            // modifyVars: { '@primary-color': '#0082c3' },
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
  webpack:{
      // 这里面是webpack的配置
    plugins: [createThemeColorReplacerPlugin()],
  }
};

utils.js其实是我个人封装的工具模块,这边把updateTheme封装进去

import client from "webpack-theme-color-replacer/client";
import { generate } from "@ant-design/colors";
// 同样的 antd-react和antd-vue获取generate方法的路径是不同的

function getAntdSerials(color) {
  // 淡化(即less的tint)
  const lightens = new Array(9).fill().map((t, i) => {
    return client.varyColor.lighten(color, i / 10);
  });
  // colorPalette变换得到颜色值
  const colorPalettes = generate(color);
  const rgb = client.varyColor.toNum3(color.replace("#", "")).join(",");
  return lightens.concat(colorPalettes).concat(rgb);
}
function changeColor(newColor) {
  var options = {
    newColors: getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors`
    changeUrl(cssUrl) {
      return `/${cssUrl}`; // while router is not `hash` mode, it needs absolute path
    },
  };
  return client.changer.changeColor(options, Promise);
}

export default {
  // 最后将修改主题的方法updateTheme暴露出去
  updateTheme(newPrimaryColor) {
    const hideMessage = () => console.log("正在切换主题!", 0);
    changeColor(newPrimaryColor).finally((t) => {
      setTimeout(() => {
        hideMessage();
      });
    });
  },
};

然后在任意组件里面地方都可以引入utils并且调用里面的updateTheme方法改变主题了(注:updateTheme方法传参直接传入十六进制的颜色,不要传其他格式的颜色,因为在做颜色淡化例如less的fade方法事时可能会报错)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值