前言
說起換膚功能,前端肯定不陌生,其實就是顏色值的更換,實現方式有很多,也各有優缺點
一、看需求是什么
一般來說換膚的需求分為兩種:
1. 一種是幾種可供選擇的顏色/主題樣式,進行選擇切換,這種可供選擇的主題切換不會很多
2. 另一種是需要自定義色值,或者通過取色板取色,可供選擇的范圍就很大了
二、如何實現
1. 對於可供選擇的顏色/主題樣式換膚的實現
-
一個全局class控制樣式切換
切換的時候js控制樣式的切換
-
JS改變href屬性值切換樣式表,例如:
<link id="skincolor" href="skin-default.css" rel="stylesheet" type="text/css">
document.getElementById('#skincolor').href = 'skin-red.css';
這種方式需要維護幾個主題樣式表,js點擊切換的時候通過改變css樣式表鏈接來實現。 例如這個 demo
這種實現對於,顏色和主題多了的時候,維護起來就很麻煩,需要同時維護 n 個樣式文件,並且使用JS改變href屬性會帶來加載延遲,樣式切換不流暢,體驗也不好。
但如果是有包含不同復雜背景圖片切換的時候,這種方式可以實現,但其他如下面要說的css變量 less modifyVars 就無法實現了
-
HTML 的 rel 屬性下的 alternate 實現: MDN Alternative style sheets
示例:
<link href="reset.css" rel="stylesheet" type="text/css">
<link href="default.css" rel="stylesheet" type="text/css" title="Default Style">
<link href="fancy.css" rel="alternate stylesheet" type="text/css" title="Fancy">
<link href="basic.css" rel="alternate stylesheet" type="text/css" title="Basic">
所有樣式表都可分為3類:
- 沒有title屬性,rel屬性值僅僅是stylesheet的
<link>
無論如何都會加載並渲染,如reset.css; - 有title屬性,rel屬性值僅僅是stylesheet的
<link>
作為默認樣式CSS文件加載並渲染,如default.css; - 有title屬性,rel屬性值同時包含alternate stylesheet的
<link>
作為備選樣式CSS文件加載,默認不渲染,如red.css和green.css;
alternate意味備用,相當於是 css 預加載進來備用,所以不會有上面那種切換延時
但怎么用呢?禁用掉?
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link
link 的 disabled 屬性
使用JavaScript代碼修改<link>
元素DOM對象的disabled
值為false
,可以讓默認不渲染的CSS開始渲染。實現 demo
2. 對於制定動態色值換膚的實現
如果是要實現動態換膚,自定義色值,那上面的幾種方式就不適合了。
先看下已有的實現有哪些方法
Element-UI 有換膚功能 示例預覽
實現原理: 官方解釋
- 先把默認主題文件中涉及到顏色的 CSS 值替換成關鍵詞:https://github.com/ElementUI/theme-preview/blob/master/src/app.vue#L250-L274
- 根據用戶選擇的主題色生成一系列對應的顏色值:https://github.com/ElementUI/theme-preview/blob/master/src/utils/formula.json
- 把關鍵詞再換回剛剛生成的相應的顏色值:https://github.com/ElementUI/theme-preview/blob/master/src/utils/color.js
- 直接在頁面上加
style
標簽,把生成的樣式填進去:https://github.com/ElementUI/theme-preview/blob/master/src/app.vue#L198-L211
看這個實現,還是比較麻煩的,想看看還有沒有更優雅的方法來實現
Ant Design 的更換主題色功能是用 less 提供的 modifyVars 的方式進行覆蓋變量來實現。
less的 modifyVars方法
modifyVars方法是是基於 less
在瀏覽器中的編譯來實現。所以在引入less文件的時候需要通過link方式引入,然后基於less.js中的方法來進行修改變量
less.modifyVars({ '@themeColor': 'blue' });
link方式引入主題色文件
<link rel="stylesheet/less" type="text/css" href="./src/less/public.less" />
更改主題色事件
// color 傳入顏色值 handleColorChange (color) { less.modifyVars({ // 調用 `less.modifyVars` 方法來改變變量值'
@themeColor':color }) .then(() => { console.log('修改成功'); }); };
如果發現項目運行報錯如下:
.bezierEasingMixin(); ^ Inline JavaScript is not enabled. Is it set in your options?
那可能是沒有開啟 javascriptEnabled:true
在webpack配置里開啟
{ test: /\.less$/, loader: 'less-loader', options: { javascriptEnabled: true } },
less方法僅限於用less的項目才能使用,查了下sass是沒有類似 less.modifyVars 這種方法的。
那有沒有通用一點的方法呢?於是就有了
css 變量方法
如果項目里用的不是less, 那么還是用css的方法,通用且容易操作,使用css變量來進行主題色的修改,替換主題色變量,然后用setProperty來進行動態修改
用法就是給變量加--前綴,涉及到主題色的都改成var(--themeColor)這種方式
用之前看下兼容性
https://caniuse.com/#search=CSS%20Variables
大部分主流瀏覽器還是支持的,而且主要是操作起來夠簡便。
用法舉例:
body{ --themeColor:#000; }
使用:
.main{ color: var(--themeColor); }
要修改主題色的話:
document.body.style.setProperty('--themeColor', '#ff0000');
總結
至此,總結了一些換膚的實現方案,大家如果有更好的方案,歡迎補充哦~
參考:
https://juejin.im/post/5ca41617f265da3092006155
https://www.zhangxinxu.com/wordpress/2019/02/link-rel-alternate-website-skin/