您好,欢迎访问一九零五行业门户网

Ant Design中如何定制动态主题?聊聊实现方法

ant design中如何定制动态主题?本篇文章给介绍一下在 ant design 中定制主题和实现动态主题的方法,希望对大家有所帮助!
一、前言hey 各位,好久不见,最近在项目中遇到了动态主题的需求,所以就顺手记录一下实现过程。本文篇幅可能有些长,请耐心观看
二、环境准备为了方便,本文中示例使用了 create-react-app + caco + craco-less 实现:
# 创建项目create-react-app demo# 安装必要依赖yarn add antd # 记得要装最新版yarn add -d @craco/craco craco-less babel-plugin-import
将 package.json 中的 npm-script 修改一下即可:
{ scripts: { "start": "craco start" }}
顺便添加下初始的 craco.config.js:
const cracolessplugin = require('craco-less');module.exports = { plugins: [{ plugin: cracolessplugin }],};
然后,改一下 app.js:
import { usestate } from 'react';import { avatar, card, button, space, switch } from 'antd';function app() { const [theme, settheme] = usestate('light'); const checked = theme === 'light'; const handlethemechange = (checked) => { settheme(checked ? 'light' : 'dark'); }; return ( <div classname="app"> <card title={<avatar size="large" src={logo} />}> <space> <switch checked={checked} checkedchildren="亮" uncheckedchildren="暗" onchange={handlethemechange} /> <button type="primary">动态主题</button> </space> </card> </div> );}export default app;
然后启动就可以看到如下界面:
至于为何没有样式,各位可以看之前的文章 《vite 能满足你吗?》
https://mp.weixin.qq.com/s/68e7cbxrhad4u5kat99noa
三、如何引入主题?在 ant design 中,有很多种姿势引入主题,下面先来简单的整理一下。
1. 引入样式文件
直接在 app.js 中引入样式文件:
// app.jsimport 'antd/dist/antd.css';
但既然使用了 craco-less 那就使用 ant design 的 less 文件吧,但是当你直接引入 antd.less 时,会得到如下错误:
解决这个问题很简单,只需要在 lessoption 中将 javascriptenabled 打开即可:
const cracolessplugin = require('craco-less');module.exports = { plugins: [ { plugin: cracolessplugin, options: { lessloaderoptions: { lessoptions: { javascriptenabled: true, }, }, }, }, ],};
然后就可以看到如下界面:
【相关推荐:《ant design pro应用》】
当然你也可以选择在 app.less 中引入:
@import '~antd/dist/antd.less';
2. babel-plugin-import
之前的文章中说过如何使用 babel-plugin-import,配合 craco 的使用步骤是一样的,只不过需要在 craco.config.js 中添加对应的配置即可:
// craco.config.jsmodule.exports = { babel: { plugins: [ ['import', { libraryname: 'antd', librarydirectory: 'es', style: true }], ], }, // ...};
这样就可以删掉上面在 app.js/app.less 引入的样式了。
四、如何实现定制主题和动态主题?上面两种方式就是最常用的引入 ant design 样式的方式,下面讲基于上面的方式来讲解如何修改覆盖原有样式。
其实可以发现在 antd/dist 中提供了很多种样式文件:
├── antd.compact.less├── antd.dark.less├── antd.less├── antd.variable.less├── compact-theme.js├── dark-theme.js├── default-theme.js├── theme.js└── variable-theme.js
antd.(dark|compact).less 两个文件分别是黑暗和紧凑模式的样式,而 antd.variable.less 文件则是最新版 ant design 才有的文件,它有什么用下面会说到,除了 .less 样式文件之外各位会发现还有几个 xxx-theme.js 文件,如果你打开会发现里面其实都是各个主题的变量值,以 dark-theme.js 为例:
const darkthemesingle = { "theme": "dark", "popover-background": "#1f1f1f", "popover-customize-border-color": "#3a3a3a", "body-background": "@black", "component-background": "#141414", // ...};
如何使用下面会讲,现在就开始本文的主题:实现定制主题和动态主题。
1. 定制主题
如果你想实现定制主题,那很简单,简单来说就是 样式覆盖,比如使用黑暗主题就直接引入黑暗模式的样式文件,比如在默认主题下想修改主色调一般有两种方式:通过修改样式文件或通过 lessoption。
// 1. 通过修改样式文件// app.less@import '~antd/dist/antd.less';@primary-color: green;
// 2. 通过 lessoptions// craco.config.jsmodule.exports = { // ... plugins: [ { plugin: cracolessplugin, options: { lessloaderoptions: { lessoptions: { modifyvars: { 'primary-color': 'cyan', }, javascriptenabled: true, }, }, }, }, ],};
需要说一下,如果同时使用了这两种定制主题的方式,则只会应用第二种通过 modifyvars 设置的内容。如果你问我为啥,那我就额外插一节 less 讲解的课吧
less 小课堂
前提:在 app.less 中引入 antd.less 并且覆盖 @primary-color(以上面配置的 green 为例)
less 提供了命令行让我们可以通过 terminal 命令将 .less 文件转义为 css:
# 转义 less 文件npx lessc ./src/app.less ./src/app.css --js
让我们看一下 app.css 中的 primary-color 是什么:
.ant-btn-primary { border-color: green; background: green;}
可以看到 primary-color 设为 green 生效了,我们再加上 modifyvars 看下呢?
npx lessc ./src/app.less ./src/app.css --js --modify-var="primary-color: cyan"
在看下生成的 app.css 嘞:
.ant-btn-primary { border-color: cyan; background: cyan;}
wow~竟然和我们本地开发时一样替换成了 modifyvars 中的内容!这又是为啥呢?
我们进入 node_modules/.bin/lessc 文件,在 parselessfile 中 console 一下 data 和 options 内容会得到源文件字符串和命令行中的一些配置,在此我们会得到:
# data@import 'antd/dist/antd.less';@primary-color: green;.app { text-align: center;}# options{ javascriptenabled: true, modifyvars: { 'primary-color': 'cyan' }}
随后我们再进入 node_modules/less/lib/less/render.js 文件,进入 render 方法可以看到:
var parsetree = new parsetree(root, imports);
这一步是将 less 转为 ast,让我们来看一下转换后的 ast:
console.log(parsetree.root.rules);// rules ast[ // ... node { name: '@primary-color', value: node { value: 'green' } }, node { name: '@primary-color', value: node { value: 'cyan' } }, // ...]
这样是不是可以理解了?就是 modifyvars 中的变量覆盖了样式文件中的变量。下课!
再回到定制主题的相关内容,现在说下如何使用上面说到的 darksingletheme,我们可以看下 theme.js 的内容:
function getthemevariables(options = {}) { let themevar = { 'hack': `true;@import "${require.resolve('antd/lib/style/color/colorpalette.less')}";`, ...defaulttheme }; if(options.dark) { themevar = { ...themevar, ...darkthemesingle } } if(options.compact){ themevar = { ...themevar, ...compactthemesingle } } return themevar;}
可以看到,如果我们在使用 getthemevariables 时将 dark 或 compact 设为 true 就能应用上对应的样式,我们来试下:
// craco.config.jsmodule.exports = { // ... plugins: [ { plugin: cracolessplugin, options: { lessloaderoptions: { lessoptions: { modifyvars: { ...getthemevariables({ dark: true, }), }, javascriptenabled: true, }, }, }, }, ],};
就是这么简单,在使用 getthemevariables 时也可以搭配前面所说的 modifyvars 来覆盖样式。这就是常用的定制主题的方式,就是之前所说的 覆盖变量,总结一下这两种方式:
引入主题样式文件并覆盖对应 less 变量;
使用 getthemevariables 引入对应主题,通过 modifyvars 覆盖变量值;
2. 动态主题
到现在 ant design 文档都没有出切换亮/暗模式的功能,但在文档中却有提到相应功能。在本文中主要介绍3种方式,其中一种就是官方出的 动态主题(实验性)。
i. 动态切换样式文件
切换样式文件,这应该是最容易想到的一个方案,ant design 本来就提供了例如 default/dark/compact 主题的样式,我们只需要将这些文件保存在我们的项目中,按需切换即可,这个方案不赘述,实现起来也十分简单。
ii. configprovider
configprovider 是 ant design 提供的一个实验性的动态主题方案,使用很简单,在入口 .less 文件中引入 variable.less 文件,然后在 configprovider 中复写对应变量,具体使用如下:
// app.less@import 'antd/dist/antd.variable.less';
默认样式与 primary 一样,然后我们使用 configprovider 修改主题色(还是以 primarycolor 为例):
// app.js// 增加下面一行configprovider.config({ theme: { primarycolor: 'aquamarine' } });
动态切换我们与上面使用方式一致:
// app.jsconfigprovider.config({ theme: { primarycolor: checked ? 'aquamarine' : 'darkgreen', },});
这么方便,这么好用,他有什么不足之处么?有,但只要你不介意其实问题不大。通过 configprovider 可以配置的颜色很有限:
// node_modules/antd/es/config-provider/context.d.tsinterface theme { primarycolor?: string; infocolor?: string; successcolor?: string; processingcolor?: string; errorcolor?: string; warningcolor?: string;}
可以看到,通过这种方式来配置的颜色仅有上面六种,但如果你想 extends theme 来添加其他字段,那不好意思,行不通,再来看下它是如何处理这几种颜色的:
/** * @param {string} colorval * @param {string} type */var fillcolor = function fillcolor(colorval, type) { var basecolor = new tinycolor(colorval); var colorpalettes = generate(basecolor.torgbstring()); variables["".concat(type, "-color")] = formatcolor(basecolor); variables["".concat(type, "-color-disabled")] = colorpalettes[1]; variables["".concat(type, "-color-hover")] = colorpalettes[4]; variables["".concat(type, "-color-active")] = colorpalettes[7]; variables["".concat(type, "-color-outline")] = basecolor.clone().setalpha(0.2).torgbstring(); variables["".concat(type, "-color-deprecated-bg")] = colorpalettes[1]; variables["".concat(type, "-color-deprecated-border")] = colorpalettes[3];};// 使用如下fillcolor(theme.successcolor, 'success');
所以他只是对这几种颜色进行了特定处理,而对于其它的颜色(比如组件背景色)等并未作处理,但即使某些颜色的命名方式也符合这种规范,也不会奏效,毕竟 ant design 使用了 if (theme.successcolor) 这种方式来条件修改这些颜色。
iii. css variables
我打算在这一部分来介绍 ii. configprovider 中 antd.variable.less 的内容,因为这个方法与 ant design 提供的 configprovider 本质上有些类似:通过 css variables 来修改全局的颜色。我们打开对应文件来简单看下内容:
// node_modules/antd/lib/style/themes/variable.lesshtml { @base-primary: @blue-6; --@{ant-prefix}-primary-color: @base-primary; --@{ant-prefix}-primary-color-hover: color(~`colorpalette('@{base-primary}', 5) `); --@{ant-prefix}-primary-color-active: color(~`colorpalette('@{base-primary}', 7) `); --@{ant-prefix}-primary-color-outline: fade(@base-primary, @outline-fade); // ...}
上面的代码中涉及了 less 中几个比较基本的语法:variable interpolation 和 escaping。
具体内容可以看上面截图,我就不再赘述,ant design 就通过这种方式实现了动态主题,将 color 值设置为可变的 css variables。既然 configprovider 可变的 color 有限,那我们就自己来定义这些颜色吧~这种方式实现起来比较复杂,但是可自定义性就无限可能了
// app.less:root { --main-color: green;}.ant-btn { &.ant-btn-primary { border-color: ~'var(--main-color)'; background: ~'var(--main-color)'; }}
看一下通过这种方式实现的效果:
如何实现修改 :root 中的样式呢?具体 ant design 的实现各位可以看 node_modules/antd/es/config-provider/cssvariable.js 和 node_modules/rc-util/es/dom/dynamiccss.js 两个文件,内容十分简单。我先写了一个简易版:
const dark = { '--main-color': 'darkgray',};const light = { '--main-color': 'green',};const themes = { dark, light };const changetheme = (theme) => { const nexttheme = themes[theme]; object.keys(nexttheme).foreach((key) => { document.documentelement.style.setproperty(key, nexttheme[key]); });};
changetheme 方法就是修改 :root 中样式的方法。
但为什么不直接在 app.less 中采用 @primary-color: ~'var(--main-color)' 的形式,非要重写组件样式呢?
如果你去看 ant design 中样式文件的源码你会发现其中用到了很多 function,比如 less 中的 fade 函数:
set the absolute opacity of a color. can be applied to colors whether they already have an opacity value or not.
来自 less 官网:https://lesscss.org/functions/#color-operations-fade
如果我们采用刚才说的那种形式来修改 @primary-color 等样式,less 就会抛出异常:argument cannot be evaluated to a color:
// node_modules/less/lib/less/functions/color.js// fade 方法function fade (color, amount) { var hsl = tohsl(color); hsl.a = amount.value / 100; hsl.a = clamp(hsl.a); return hsla(color, hsl);}// tohsl 方法function tohsl(color) { // 此处的 color.tohsl 函数是下面 color.js 中的 tohsl 函数 if (color.tohsl) { return color.tohsl(); } else { throw new error('argument cannot be evaluated to a color'); }}// node_modules/less/lib/less/tree/color.jsfunction tohsl () { var r = this.rgb[0] / 255, g = this.rgb[1] / 255, b = this.rgb[2] / 255, a = this.alpha; // ...},
这样就可以看出如果我们传给 fade 函数的不是一个准确的颜色值,在 color.js 中是获取不到 rgb[0] 等值的,所以在 less 编译过程中就会直接报错。
更多编程相关知识,请访问:编程视频!!
以上就是ant design中如何定制动态主题?聊聊实现方法的详细内容。
其它类似信息

推荐信息