随着业务的发展,客户的需求也会变得更加多样化,产品后期就需要有自定义界面的能力,于是出现了“动态换主题”的需求。
设计部门的同事让我们可以参考Ant Design色板生成算法演进之路
后面我们动态计算色板也是采用了目前 Ant Design
的算法, @ant-design/colors
但是切换主题的方式,经验证并不能很完美的适用于我们微前端项目。
以上色系变量表是我们本次最终需要的全部变量
其中每种色系分为两种,h开头的和a开头的,a开头的通过调整透明度来生成,h 开头的一组由 base 色通过ant-design 的动态计算生成
本色系设计由合思设计团队
出品,中性色为直接定义死的,不做计算;
可配置的基础色分为
我在接到需求后,经过和公司架构师及其他同事的探讨后,渐渐产出了以下几种方案,一步步踩坑过来。
两种主题模式(light/dark),需要分别两个 less 文件来定义这两套颜色变量
Light-colors.less
dark-colors.less
两种模式下,值固定不变的颜色变量单独定义一个文件 common-colors.less
,然后我选择将三个文件引入到同一个index
中输出使用,需要使用的地方只需要引入index.less 即可。
但是问题来了
1、如何在index.less 中来判断使用light-colors 还是 dark-colors 呢?
@import 只能定义在文件顶部,也没有任何可以做条件引入的方法
2、如何根据品牌色动态计算色系变量值呢?
计算为色系变量值是通过js产出一个数组,想要导入到一个less文件中,再引入使用,想要动态切换的话,需要用到 less的modifyVars方法, 也是Ant Design
官方提供的方式,接着我们尝试
less
的modifyVars方法是是基于 less
在浏览器中的编译来实现。所以在引入less文件的时候需要通过link方式引入,然后基于less.js中的方法来进行修改变量
less.modifyVars({
'@themeColor': '#22B2CC'
});
<link rel="stylesheet/less" type="text/css" href="./src/less/theme-colors.less" />
// color 传入颜色值
changeTheme (color) {
less.modifyVars({ // 调用 `less.modifyVars` 方法来改变变量值'
@themeColor':color
})
.then(() => {
console.log('修改成功');
});
};
<span style="font-size: 14px;">less编译器
,太大了,严重影响性能;<span style="font-size: 14px;">webpack
配置,无法多个进程间共享变量,不适用于微前端项目。<span style="font-size: 14px;">less
的项目才能使用,如果你项目使用的是<span style="font-size: 14px;">sass
,是没有类似 <span style="font-size: 14px;">less.modifyVars
这种解决方案的。1、在webpack构建时,通过 webpack-theme-color-replacer这个插件从所有输出的css文件中提取主题颜色样式,并创建一个仅包含颜色样式的'theme-colors.css'文件。在网页的运行时,客户端部分下载此css文件,然后将颜色动态替换为新的自定义颜色,能够满足更灵活丰富的功能场景,性能出色。
2、@ant-design/colors 来动态计算出品牌色系和功能色系。
3、可以动态的切换品牌色来获取整个主题的切换。
色系通过 提供的基准色, 自动计算及输出的颜色集合:
通过计算就可以输出整个色系数组如下:
需要设置颜色的地方就可以直接使用定义的这些变量,需要切换主题或者颜色的时候,传入主题模式、品牌色重新计算,就可以实现动态切换主题了。
看似没啥问题,但是在我们的系统里,问题来了。
因为我们是微前端项目,拆包出大概二三十个项目,创建一个仅包含颜色样式的theme-colors.css
文件这一步是运行在编译时的,那么每个子项目如果没有配置这个webpack,就无法共享该变量,在开发编译阶段就会报错!即使每个项目都配置了这样的webpack构建,也会创建各自的 theme-colors.css
文件,更改主题时候也无法同步切换,一样的坑爹!!!
由此可见,即使一个方案很好很成熟,也不是满足所有项目的。落实一个方案的时候,要根据自己的项目情况做分析,做出一个符合自身项目的解决方案才是硬道理,而不是一味的生搬硬套。
于是该方案毙掉,继续思考下一个方案。
时代好了,浏览器普遍支持Css3变量了,基于Css3 Variable 共享全局主题变量看起来就是一个很通用的方案了。
首先定义一个全局变量,改变这个变量的值,页面中所有引用这个变量的元素都会进行改变,既没有 less
的编译过程,也不存在什么性能问题,这不就是我们最期望的动态换肤方案吗?
Css3 Variable
的用法就是给变量加--前缀,涉及到主题色的都改成var(--themeColor)这种方式
我们先查一下兼容性
主流浏览器基本全部兼容,对于大多数互联网企业产品完全够用了,但是对于某些还在使用IE
浏览器的产品就需要ponyfill
方案兼容了。
也确实有这样一个 polyfill
能兼容IE: css-vars-ponyfill
这个polyfill
只会在不支持Css3 Variable
的环境会生效
我们开始写代码了:
1、建一个存放公共css变量的js文件(variable.js),将需要定义的css变量存放到该js文件,品牌色及功能色等通过antd算法计算获得;
import { getAlphaColor } from "./themeUtils";
const { generate } = require("@ant-design/colors");
import baseTheme from "./baseTheme";
import lightTheme from "./lightTheme";
import darkTheme from "./darkTheme";
import { functionalColorsBase, grayBase } from "./colors";
const themeModes = {
light: undefined,
dark: {
theme: "dark",
backgroundColor: grayBase,
},
};
// 获取品牌色系
export const getBrandColors = (color, mode) => {
let options = themeModes[mode];
return generate(color, options);
};
// 获取功能色系
export const getFunctionalColors = (mode) => {
let options = themeModes[mode];
let { success, warning, danger, info } = functionalColorsBase;
const successColors = generate(success, options);
const warningColors = generate(warning, options);
const dangerColors = generate(danger, options);
const infoColors = generate(info, options);
return {
success: successColors,
warning: warningColors,
danger: dangerColors,
info: infoColors,
};
};
// 输出色板
export const modifyVars = (color, mode) => {
const brandColors = getBrandColors(color, mode);
const { success, warning, danger, info } = getFunctionalColors(mode);
const colors = {
...baseTheme,
"--brand-base": brandColors[5],
"--success-base": success[5],
"--warning-base": warning[5],
"--danger-base": danger[5],
"--info-base": info[5],
"--h-brand-1": brandColors[0],
"--h-brand-2": brandColors[1],
"--h-brand-3": brandColors[2],
"--h-brand-4": brandColors[3],
"--h-brand-5": brandColors[4],
"--h-brand-6": brandColors[5],
"--h-brand-7": brandColors[6],
"--h-brand-8": brandColors[7],
"--h-brand-9": brandColors[8],
"--h-brand-10": brandColors[9],
"--h-success-1": success[0],
"--h-success-2": success[1],
"--h-success-3": success[2],
"--h-success-4": success[3],
"--h-success-5": success[4],
"--h-success-6": success[5],
"--h-success-7": success[6],
"--h-success-8": success[7],
"--h-success-9": success[8],
"--h-success-10": success[9],
"--h-warning-1": warning[0],
"--h-warning-2": warning[1],
"--h-warning-3": warning[2],
"--h-warning-4": warning[3],
"--h-warning-5": warning[4],
"--h-warning-6": warning[5],
"--h-warning-7": warning[6],
"--h-warning-8": warning[7],
"--h-warning-9": warning[8],
"--h-warning-10": warning[9],
"--h-danger-1": danger[0],
"--h-danger-2": danger[1],
"--h-danger-3": danger[2],
"--h-danger-4": danger[3],
"--h-danger-5": danger[4],
"--h-danger-6": danger[5],
"--h-danger-7": danger[6],
"--h-danger-8": danger[7],
"--h-danger-9": danger[8],
"--h-danger-10": danger[9],
"--h-info-1": info[0],
"--h-info-2": info[1],
"--h-info-3": info[2],
"--h-info-4": info[3],
"--h-info-5": info[4],
"--h-info-6": info[5],
"--h-info-7": info[6],
"--h-info-8": info[7],
"--h-info-9": info[8],
"--h-info-10": info[9],
};
const darkConfigableTheme = {
"--a-brand-1": getAlphaColor(brandColors[5], 0.04),
"--a-brand-2": getAlphaColor(brandColors[5], 0.08),
"--a-brand-3": getAlphaColor(brandColors[5], 0.16),
"--a-brand-4": getAlphaColor(brandColors[5], 0.24),
"--a-brand-5": getAlphaColor(brandColors[5], 0.32),
"--a-brand-6": getAlphaColor(brandColors[5], 0.4),
"--a-brand-7": getAlphaColor(brandColors[5], 0.52),
"--a-brand-8": getAlphaColor(brandColors[5], 0.64),
"--a-brand-9": getAlphaColor(brandColors[5], 0.76),
"--a-brand-10": getAlphaColor(brandColors[5], 0.88),
"--a-success-1": getAlphaColor(success[5], 0.04),
"--a-success-2": getAlphaColor(success[5], 0.08),
"--a-success-3": getAlphaColor(success[5], 0.16),
"--a-success-4": getAlphaColor(success[5], 0.24),
"--a-success-5": getAlphaColor(success[5], 0.32),
"--a-success-6": getAlphaColor(success[5], 0.4),
"--a-success-7": getAlphaColor(success[5], 0.52),
"--a-success-8": getAlphaColor(success[5], 0.64),
"--a-success-9": getAlphaColor(success[5], 0.76),
"--a-success-10": getAlphaColor(success[5], 0.88),
"--a-warning-1": getAlphaColor(warning[5], 0.04),
"--a-warning-2": getAlphaColor(warning[5], 0.08),
"--a-warning-3": getAlphaColor(warning[5], 0.16),
"--a-warning-4": getAlphaColor(warning[5], 0.24),
"--a-warning-5": getAlphaColor(warning[5], 0.32),
"--a-warning-6": getAlphaColor(warning[5], 0.4),
"--a-warning-7": getAlphaColor(warning[5], 0.52),
"--a-warning-8": getAlphaColor(warning[5], 0.64),
"--a-warning-9": getAlphaColor(warning[5], 0.76),
"--a-warning-10": getAlphaColor(warning[5], 0.88),
"--a-danger-1": getAlphaColor(danger[5], 0.04),
"--a-danger-2": getAlphaColor(danger[5], 0.08),
"--a-danger-3": getAlphaColor(danger[5], 0.16),
"--a-danger-4": getAlphaColor(danger[5], 0.24),
"--a-danger-5": getAlphaColor(danger[5], 0.32),
"--a-danger-6": getAlphaColor(danger[5], 0.4),
"--a-danger-7": getAlphaColor(danger[5], 0.52),
"--a-danger-8": getAlphaColor(danger[5], 0.64),
"--a-danger-9": getAlphaColor(danger[5], 0.76),
"--a-danger-10": getAlphaColor(danger[5], 0.88),
"--a-info-1": getAlphaColor(info[5], 0.04),
"--a-info-2": getAlphaColor(info[5], 0.08),
"--a-info-3": getAlphaColor(info[5], 0.16),
"--a-info-4": getAlphaColor(info[5], 0.24),
"--a-info-5": getAlphaColor(info[5], 0.32),
"--a-info-6": getAlphaColor(info[5], 0.4),
"--a-info-7": getAlphaColor(info[5], 0.52),
"--a-info-8": getAlphaColor(info[5], 0.64),
"--a-info-9": getAlphaColor(info[5], 0.76),
"--a-info-10": getAlphaColor(info[5], 0.88),
};
const lightModeColors = { ...lightTheme, ...colors };
const darkModeColors = { ...darkTheme, ...darkConfigableTheme, ...colors };
console.log(lightModeColors, "=====", darkModeColors);
return mode == "light" ? lightModeColors : darkModeColors;
};
2、页面使用css变量,无论是web主项目,还是各个plugin子项目都可以共享变量,不需要引入任何依赖,设计图标注与代码对应关系:
UI | CODE |
---|---|
h-brand-1 | var(--h-brand-1) |
3、封装切换主题的js,在项目入口做初始化调用,支持更改light和dark模式,及变更品牌色基准色
import { brandBase, modifyVars } from "./variable";
import cssVars from "css-vars-ponyfill";
const key = "data-theme";
// 获取当前主题
export const getTheme = (mode, color) => {
const localTheme = localStorage.getItem(key);
const dataTheme = localTheme
? JSON.parse(localTheme)
: {
color: color || brandBase,
mode: mode || "light",
};
return dataTheme;
};
// 初始化主题
export const initTheme = (mode, color) => {
const dataTheme = getTheme(mode, color);
document.documentElement.setAttribute("data-theme", dataTheme.mode);
cssVars({
watch: true,
// 当添加,删除或修改其<link>或<style>元素的禁用或href属性时,ponyfill将自行调用
variables: modifyVars(dataTheme.color, dataTheme.mode), // variables 自定义属性名/值对的集合
onlyLegacy: false, // false 默认将css变量编译为浏览器识别的css样式 true 当浏览器不支持css变量的时候将css变量编译为识别的css
});
};
// 变更主题
export const changeTheme = (mode, color) => {
const dataTheme = {
color: color || brandBase,
mode: mode || "light",
};
localStorage.setItem(key, JSON.stringify(dataTheme));
document.documentElement.setAttribute("data-theme", dataTheme.mode);
cssVars({
watch: true,
variables: modifyVars(dataTheme.color, dataTheme.mode),
onlyLegacy: false,
});
};
4、在切换主题的按钮组件中调用 changeTheme切换主题
最终效果,目前只有部分扫雷了部分页面,控制开关为临时征用侧边栏:
至此,一个微前端项目的动态换肤方案已经实现,大家如果有更好的方案,欢迎补充哦~
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/cnU1PJWyI7Dswr4A8RIlXg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。