背景
- 需求背景:動態(tài)配置頁面主題相關(guān)顏色,而不是定制主題模式(類似黑夜模式,白天模式)。
- 兼容性問題:在 IE 中
var()
報錯顯示異常成透明色,(css-vars-ponyfill 插件 夕土,使頁面展示一個默認色) - 第三方 UI 庫支持 CSS Variable,(antd@4.17.0-alpha.0 以上瘟判,mkui-fd@4.1.8-beta 以上 )
1. 實現(xiàn)原理
1.1. 自定義屬性 (--*): CSS 變量
帶有前綴 -- 的屬性名怨绣,比如--example--name
,表示的是帶有值的自定義屬性拷获,其可以通過var
函數(shù)在全文檔范圍內(nèi)復(fù)用的篮撑。
CSS 自定義屬性是可以級聯(lián)的:每一個自定義屬性可以多次出現(xiàn),并且變量的值將會借助級聯(lián)算法和自定義屬性值運算出來匆瓜。
注意赢笨,規(guī)則集所指定的選擇器定義了自定義屬性的可見作用域。通常的最佳實踐是定義在根偽類:root
下驮吱,這樣就可以在 HTML 文檔的任何地方訪問到它茧妒。
:root
這個 CSS 偽類匹配文檔樹的根元素。對于 HTML 來說左冬,:root
表示 <html>
元素桐筏,除了優(yōu)先級更高之外,與 html
選擇器相同拇砰。
1.2. ConfigProvider.config() (config-provider/index.js)
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); // 更改主題色
}
1.3. registerTheme() (node_modules/mkui-fd/lib/config-provider/cssVariables.js)
創(chuàng)建所有 css 變量梅忌,可參考簡易版 customCssVariable.js
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) {
// 轉(zhuǎn)成 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 更新業(yè)務(wù)代碼中的主題色: 創(chuàng)建 style 標簽,值為cssList除破,并作為最后一個子元素插在head標簽下
updateCSS(
'\n :root {\n '.concat(cssList.join('\n'), '\n }\n '),
'-mkui-fd-'.concat(Date.now(), '-').concat('-dynamic-theme'),
);
}
1.4. updateCSS() (node_modules/rc-util/lib/Dom/dynamicCSS.js)
創(chuàng)建 style 標簽牧氮,值為 cssList,作為最后一個子元素插在 head 標簽下瑰枫,詳見 node_modules/rc-util/lib/Dom/dynamicCSS.js
1.5. 動態(tài)改變顏色另外一種寫法(我們項目中不推薦)
在 body 標簽中增加 style 屬性踱葛,改變 body 中所有 --mkui-primary-color 使用值, 優(yōu)先級高于 html
document.body.style.setProperty('--mkui-primary-color', '#ffff00');
↓ ↓ ↓ ↓ ↓ ↓
<body style="--mkui-primary-color:#ffff00;">
2. 如何使用
2.1 替換當(dāng)前項目引入樣式文件為 CSS Variable 版本,并在 .babel.config 中去除 babel-plugin-import 配置躁垛。
{ "libraryName": "antd" }
import { Button } from 'antd';
ReactDOM.render(<Button>xxxx</Button>);
↓ ↓ ↓ ↓ ↓ ↓
var button = require('antd/lib/button');
ReactDOM.render(<button>xxxx</button>);
{ "libraryName": "antd", style: "css" }
import { Button } from 'antd';
ReactDOM.render(<Button>xxxx</Button>);
↓ ↓ ↓ ↓ ↓ ↓
var button = require('antd/lib/button');
require('antd/lib/button/style/css');
ReactDOM.render(<button>xxxx</button>);
{ "libraryName": "antd", style: true }
import { Button } from 'antd';
ReactDOM.render(<Button>xxxx</Button>);
↓ ↓ ↓ ↓ ↓ ↓
var button = require('antd/lib/button');
require('antd/lib/button/style');
ReactDOM.render(<button>xxxx</button>);
注意:
Antd 默認支持基于 ES modules 的 tree shaking剖毯,對于 js 部分,直接引入 import { Button } from 'antd'
就會有按需加載的效果教馆。
如今webpack,rollup等構(gòu)建工具都具備了搖樹功能(Tree Shaking), 其原理是利用ESM模塊的import語法的特性,通過AST語法樹進行分析然后去除未使用到的代碼擂达。但tree shaking方式也只是處理了js部分土铺,對于組件的css加載還需要手動引入.
2.2 引入包含css 變量的組件庫 css 文件(app.js)
import 'mkui-fd/dist/mkui-fd.variable.min.css';
import 'mkui-ext/dist/mkui-ext.variable.min.css';
2.3.定義顏色變量(variables.less)
- 使用 mkui-fd 組件中已有變量
:root 下已經(jīng)默認生成了部分可全局使用的變量:--mkui-primary-1, ... --mkui-primary-10
使用 var()
插入 CSS 變量的值
@theme-color: var(--mkui-primary-color, #1890ff);
@theme-color-selected-bg: var(--mkui-primary-1, #f0f8ff);
-
自定義創(chuàng)建可全局使用的 CSS 變量
// color.less
為了兼容 IE 使用 css-vars-ponyfill, 必須使用:root 而不是 html
:root {
--theme-color: #1890ff;
--theme-color-selected-bg: #f0f8ff;
}
// variables.less
@theme-color: var(--theme-color);
@theme-color-selected-bg: var(--theme-color-selected-bg, #f0f8ff);
2.4. 調(diào)用配置方法
1) 調(diào)用 ConfigProvider 配置方法設(shè)置主題色(cssVariable.js):
// 修改 mkui-fd 組件顏色
ConfigProvider.config({
theme: { primaryColor: themeColor },
});
// 修改 mkui-ext 組件顏色
ExtConfigProvider.config({
theme: { primaryColor: themeColor },
});
Note: primaryColor: 組件基本色胶滋,successColor
,warningColor
悲敷,errorColor
究恤,infoColor
特定情況下的顏色。
2) 對于自定義顏色變量后德,動態(tài)更改自定義變量顏色(customCssVariable.js)
setThemeColor({
themeColor,
varName: 'theme-color'
})
2.5. 色值相關(guān)
- 與主題色相關(guān)的顏色部宿,設(shè)計圖中會給一個主色(C6),及對應(yīng)的10個色階階梯的哪一階
3. 注意點:
-
為什么是
:root
而不是html
CSS 不僅用于樣式化 HTML 文檔, 它也用于 XML 和 SVG 文件瓢湃。對于 XML 和 SVG 文件理张,
:root
不是選擇html
元素,而是選擇它們的根(例如 SVG 文件中的 svg 標簽)绵患。:root
選擇器優(yōu)先級更高有助于將使用的 CSS 變量與項目中使用樣式的選擇器分開
css-vars-ponyfill 中要求:自定義屬性聲明支持僅限于
:root
和:host
規(guī)則集
-
fade()雾叭,dark()
等函數(shù)不支持變量參數(shù)fade('1199ff', 90%); fade(var(--theme-color), 90%); // 錯誤使用
mkui-fd
,mkui-ext
以及項目中顏色相關(guān)樣式,不要使用內(nèi)聯(lián)和 !important
- IE 11下取色器組件
react-color@2.19.3
報錯落蝙,降低react-color@2.18.1
組件版本
SCRIPT438: Object doesn't support property or method 'contains'
4. 其他擴展點
4.1 polyfill 和 ponyfill 的區(qū)別
polyfill 在原有的墻壁上打補丁织狐,ponyfill 的策略則是另起爐灶,不會在原有的墻壁上修補筏勒,而是重新建一面墻移迫,保證原來的墻壁還是原始純凈無污染。
例如:Array.isArray()
, 此方法 IE8 瀏覽器并不支持
polyfill 策略
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
}
ponyfill 策略
// 避免使用原生 API
// 基本上管行,為了避免全局命名的污染厨埋,Ponyfill都是建議采用獨立的模塊化的方式開發(fā)與調(diào)用的
function isArray (arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
}
什么時候使用 Ponyfill?
- 有些原生 API 完全沒法模擬,此時只能使用 Ponyfill 策略病瞳,例如 IE9 瀏覽器無法模擬 indexDB 能力揽咕,
history.pushState()
方法 - 有些原生 API 規(guī)范還沒穩(wěn)定,或者處于快速迭代中套菜,或者是瀏覽器部分支持
4.2 css-vars-ponyfill 的使用
作用:
- CSS 自定義屬性到靜態(tài)值的轉(zhuǎn)換
- 現(xiàn)代和舊版瀏覽器中運行時值的實時更新
- 轉(zhuǎn)換
<link>
亲善、<style>
和@import CSS
- 將相對
url()
路徑轉(zhuǎn)換為絕對 URL - 支持鏈式和嵌套的
var()
函數(shù)
...
限制:
- 自定義屬性聲明支持僅限于
:root
和:host
規(guī)則集-
var()
的使用僅限于屬性值(根據(jù) W3C 規(guī)范)
-
更新值:
- 在舊版瀏覽器中,ponyfill 將確定哪些
<link>
和<style>
中包含 css 變量逗柴,然后轉(zhuǎn)換成與舊版兼容的 CSS蛹头,并附加到每個元素的 DOM。 - 在支持 CSS 變量的現(xiàn)代瀏覽器中戏溺,ponyfill 將使用
style.setProperty()
接口更新值渣蜗。
請注意,當(dāng)options.onlyLegacy
為false
時旷祸,支持 CSS 變量的現(xiàn)代瀏覽器將被視為舊版瀏覽器耕拷。
Note:
{ onlyLegacy: true }
將此值設(shè)置為 false
可以在現(xiàn)代瀏覽器中模仿舊版瀏覽器,進行測試和調(diào)試托享。
4.3 HSV
1.色相
表示色彩的相貌骚烧,也就是我們常說的紅浸赫、橙、黃赃绊、綠等顏色名稱既峡。色相值按位置度量,取值為0°~360°碧查,在HSB色彩模型中紅色為0°运敢,黃色為60°,綠色為120°忠售,青色為180°传惠,藍色為240°,品紅色為300°档痪。十二色相環(huán)每一色相間距30°涉枫,二十四色相環(huán)每一色相間距15°。
2.飽和度
表示色彩的純度腐螟,取值范圍0~100%愿汰,從色環(huán)中心向外遞增。當(dāng)飽和度為0時點在中心乐纸,則顯示為灰衬廷、白、黑無彩色汽绢。當(dāng)飽和度達到100%時吗跋,點則移動到色環(huán)邊緣,會顯示每個色相最純的色光宁昭。如下圖所示跌宛,在色相(H)、亮度(B)不變的情況下減少飽和度(S)顏色逐漸變淡最后變成白色积仗。S控制混入白色的量疆拘。
3.亮度
指色彩的明亮度,取值范圍0~100%寂曹,沿著圓柱體底部向上遞增哎迄。亮度為0時即黑色,點處于最底部隆圆。當(dāng)達到100%時點上升到頂端漱挚,會顯示色相最鮮明的狀態(tài)。如下圖所示渺氧,在色相(H)旨涝、飽和度(S)不變的情況下減少亮度(B)顏色逐漸變暗最后變成黑色。B控制混入黑色的量侣背。
4.結(jié)論
1. 選取一個顏色作為主色(6 號色)颊糜;
2. 判斷減淡或加深哩治,進行顏色混合
- 若減淡秃踩,則主色與純白色(#fff)混合衬鱼,根據(jù)色號,獲取貝塞爾曲線上的對應(yīng)值憔杨。
- 若加深鸟赫,則主色與它對應(yīng)的深色混合,根據(jù)色號消别,獲取貝塞爾曲線上的對應(yīng)比例值抛蚤。加深時主色對應(yīng)的深色進行了明度與色相的調(diào)整,其中對色相的調(diào)整也就是上述引用中說的“針對冷暖色的旋轉(zhuǎn)”寻狂;
3. 分別取1~9色號的色值岁经,得到一條完整漸變色板。
參考文章