公司的项目是基于ant design开发的,年前初步完成了整个系统的动态主题切换,主要是利用了antd-theme-generator这个插件,使用 less 的 modifyVars
来更改主题。
这里要先交待一下我的依赖版本,ant design是3.19.1,antd-theme-generator是1.2.6,如果你的项目用的是ant design 4.x版本,可以直接安装最新的antd-theme-generator,如果跟我一样是接手的ant design 3.x的项目,就要和我一样用1.2.6版本的antd-theme-generator,否则后面开发的时候在生成主题less文件的时候会报错:LessError: error evaluating function darken: color.toHSL is not a function。导致无法生成主题less文件。
安装了正确版本的antd-theme-generator插件后,首先在 scripts 文件夹下添加脚本 scripts/generateColorLess.js,这个脚本执行后会生成能实现动态主题切换的less文件:
const path = require('path');
const { generateTheme } = require('antd-theme-generator');
const options = {
stylesDir: path.join(__dirname, "../src/styles"),
antDir: path.join(__dirname, "../node_modules/antd"),
varFile: path.join(__dirname, "../src/styles/variables.less"),
mainLessFile: path.join(__dirname, "../src/styles/index.less"),
themeVariables: [
"@primary-color",
"@secondary-color",
"@text-color",
"@text-color-secondary",
"@heading-color",
"@layout-body-background",
"@layout-header-background",
"@border-radius-base"
],
outputFilePath: path.join(__dirname, "../public/color.less")
};
generateTheme(options)
.then(less => {
console.log("Theme generated successfully");
})
.catch(error => {
console.log("Error", error);
});
上面的代码中主要需要了解一下options中的各个配置项:
-
stylesDir
指定了我们用来定制主题的less文件的路径; -
antDir
指定了ant design依赖的路径; -
varFile
指定了要动态切换的less变量所在的文件的路径; -
mainLessFile
指定了你编写的样式文件,也就是index.less文件的路径,要在这个文件中编写我们需要动态切换的样式,并将variables.less中的less变量应用到我们编写的样式中; -
themeVariables
数组指定所有我们自定义的需要切换的样式变量。
接着,在 src 下创建文件 src/styles/variables.less 和 src/styles/index.less。这两个文件用于定义全局的主题样式,在variables.less文件中给我们要动态切换的less变量赋初始值:
// variables.less
@import "~antd/lib/style/themes/default.less";
@primary-color: #1890ff;
注意上面的代码中引入了antd默认主题less文件,并给@primary-color
变量赋值,@primary-color
是"~antd/lib/style/themes/default.less"
中定义的变量,在variables.less中可以对ant design默认的组件或者主题样式变量进行覆盖。variables.less中赋了初始值的less变量要和generateColorLess.js中的themeVariables数组中的less变量一一对应。
在index.less中编写我们的自定义样式:
// index.less
.base-color {
color: @primary-color;
}
在需要应用@primary-color
颜色的HTML元素上添加base-color
类名,即可动态改变该HTML元素的颜色。
接下来修改 public/index.html,主要修改的部分如下:
<body>
<link rel="stylesheet/less" type="text/css" href="%PUBLIC_URL%/color.less" />
<script>
window.less = {
async: false,
env: 'development',
javascriptEnabled: true
};
</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.2/less.min.js"></script>
color.less是通过执行generateColorLess.js生成的主题样式文件,上述代码中引入了color.less,对less做了基本配置,并引入了less.js。
然后在package.json中增加执行generateColorLess.js的脚本:
"scripts": {
"start": "node ./scripts/generateColorLess.js && react-app-rewired start",
"build": "node ./scripts/generateColorLess.js && react-app-rewired build",
以上就是动态主题切换的一些基本配置,定义一个主题切换的方法,调用这个方法即可实现主题切换:
setThemeColor(theme) {
if (theme === 'default') {
window.less.modifyVars({
'@border-color-base': '#E9EEF7',
'@layout-body-background': '#f0f2f5',
'@layout-header-background': '#fffffe',
'@component-background': '#fffffe',
'@text-color': 'fade(@black, 65%)',
'@title-color': '#333',
'@table-header-bg': 'hsv(0, 0, 98%)',
'@ant-selected-color': 'rgba(0, 0, 0, 0.65)',
'@table-row-hover-bg': '#F0FDFF',
'@modal-heading-color': 'fade(#000, 85%)',
'@page-ellipsis-color': 'rgba(0, 0, 0, 0.25)',
'@empty-text-color': 'rgba(0, 0, 0, 0.25)',
'@date-last-next': 'rgba(0, 0, 0, 0.25)',
'@cursor-text-color': '#1f1f1f',
'@descriptions-title-color': 'rgba(0, 0, 0, 0.85)',
'@descriptions-label-bg-color': '#fafafa',
'@alarm-chart-bg-color': '#f2f6fa',
'@statistic-title-color': 'rgba(0, 0, 0, 0.45)',
'@dashboard-box-shadow': '0 1px 4px rgba(0, 0, 0, 0.15)'
}).catch(error => {
message.error(`Failed to update theme`);
});
} else {
window.less.modifyVars({
'@border-color-base': '#303030',
'@layout-body-background': '#000001',
'@layout-header-background': '#1f1f1f',
'@component-background': '#141414',
'@text-color': 'rgba(255, 255, 255, 0.65)',
'@title-color': 'rgba(255, 255, 255, 0.85)',
'@table-header-bg': '#1d1d1d',
'@ant-selected-color': '#1f1f1f',
'@table-row-hover-bg': '#000001',
'@modal-heading-color': 'fade(@white, 100%)',
'@page-ellipsis-color': 'rgba(255, 255, 255, 0.25)',
'@empty-text-color': 'rgba(255, 255, 255, 0.45)',
'@date-last-next': 'rgba(255, 255, 255, 0.25)',
'@cursor-text-color': '#fefefe',
'@descriptions-title-color': 'rgba(255, 255, 255, 0.85)',
'@descriptions-label-bg-color': '#101010',
'@alarm-chart-bg-color': '#0d0905',
'@statistic-title-color': 'rgba(255, 255, 255, 0.45)',
'@dashboard-box-shadow': '0 1px 4px rgba(255, 255, 255, 0.4)'
}).catch(error => {
message.error(`Failed to update theme`);
});
}
}
这里以我项目中用到的默认主题和暗黑主题为例,上述代码中提供了两套主题配色。
最后有两个需要注意的点:
- less变量在variables.less和generatorColorLess.js的
themeVariables
数组和modifyVars里都要写,否则无法实现主题切换; - 部分无法正常切换的变量要去antd依赖的default.less里看这个变量是不是直接用了别的变量,如果是的,要切换这个变量使用的那个变量。
参考资料
- mzohaibqc/antd-theme-generator
- ant design 动态切换主题
- LessError: error evaluating function darken: color.toHSL is not a function