摘要:之前在做網(wǎng)站換膚,所以今天想聊聊網(wǎng)站換膚的實(shí)現(xiàn)。一般實(shí)現(xiàn)如上圖,我們會看到在某些網(wǎng)站的右上角會出現(xiàn)這么幾個(gè)顏色塊,點(diǎn)擊不同的顏色塊,網(wǎng)站的整體顏色就被替換了。
之前在做網(wǎng)站換膚,所以今天想聊聊網(wǎng)站換膚的實(shí)現(xiàn)。網(wǎng)頁換膚就是修改顏色值,因此重點(diǎn)就在于怎么來替換。
一般實(shí)現(xiàn)
如上圖,我們會看到在某些網(wǎng)站的右上角會出現(xiàn)這么幾個(gè)顏色塊,點(diǎn)擊不同的顏色塊,網(wǎng)站的整體顏色就被替換了。要實(shí)現(xiàn)它,我們考慮最簡單的方式:點(diǎn)擊不同的按鈕切換不同的樣式表 ,如:
theme-green.css
theme-red.css
theme-yellow.css
可以看出,我們需要為每個(gè)顏色塊編寫樣式表,那如果我要實(shí)現(xiàn)幾百種或者讓用戶自定義呢,顯而易見這種方式十本笨拙,且拓展性并不高,另外,如果考慮加載的成本,那其實(shí)這種方式并不可取。
ElementUI 的實(shí)現(xiàn)ElementUI 的實(shí)現(xiàn)比上面的實(shí)現(xiàn)高了好幾個(gè)level,它能讓用戶自定義顏色值,而且展示效果也更加優(yōu)雅。當(dāng)前我的實(shí)現(xiàn)就是基于它的思路來實(shí)現(xiàn)。
我們來看看他是怎么實(shí)現(xiàn)的(這里引用的是官方的實(shí)現(xiàn)解釋):
先把默認(rèn)主題文件中涉及到顏色的 CSS 值替換成關(guān)鍵詞:https://github.com/ElementUI/theme-preview/blob/master/src/app.vue#L250-L274
根據(jù)用戶選擇的主題色生成一系列對應(yīng)的顏色值: https://github.com/ElementUI/theme-preview/blob/master/src/utils/formula.json
把關(guān)鍵詞再換回剛剛生成的相應(yīng)的顏色值:https://github.com/ElementUI/theme-preview/blob/master/src/utils/color.js
直接在頁面上加 style 標(biāo)簽,把生成的樣式填進(jìn)去:https://github.com/ElementUI/theme-preview/blob/master/src/app.vue#L198-L211
下面我具體講下我參考它的原理的實(shí)現(xiàn)過程 (我們的css 編寫是基于 postcss 來編寫的):
先確定一個(gè)主題色,其他需在在換膚過程中隨主題色一起修改的顏色值就根據(jù)主題色來調(diào)用例如(上面已經(jīng)說到了我們是基于postcss來編寫的,所以就使用了如下函數(shù)來計(jì)算顏色值): tint(var(--color-primary), 20%) ,darken(var(--color-primary), 15%) ,shade(var(--color-primary), 5%) 等。這也類似就實(shí)現(xiàn)了上面的第一步
然后根據(jù)用戶選擇的顏色值來生成新的一輪對應(yīng)的一系列顏色值:
這里我先把全部css文件中可以通過主題色來計(jì)算出其他顏色的顏色值匯總在一起,如下:
// formula.js const formula = [ { name: "hoverPrimary", exp: "color(primary l(66%))", }, { name: "clickPrimary", exp: "color(primary l(15%))", }, { name: "treeBg", exp: "color(primary l(95%))", }, { name: "treeHoverBg", exp: "color(primary h(+1) l(94%))", }, { name: "treeNodeContent", exp: "color(primary tint(90%))", }, { name: "navBar", exp: "color(primary h(-1) s(87%) l(82%))", } ]; export default formula;
這里的color函數(shù) 是后面我們調(diào)用了 css-color-function 這個(gè)包,其api使然。
既然對應(yīng)關(guān)系匯總好了,那我們就來進(jìn)行顏色值的替換。在一開始進(jìn)入網(wǎng)頁的時(shí)候,我就先根據(jù)默認(rèn)的主題色根據(jù) formula.js 中的 計(jì)算顏色匯總表 生成對應(yīng)的顏色,以便后面的替換,在這過程中使用了css-color-function 這個(gè)包,
import Color from "css-color-function"; componentDidMount(){ this.initColorCluster = ["#ff571a", ...this.generateColors("#ff571a")]; // 拿到所有初始值之后,因?yàn)槲覀円龅氖亲址鎿Q,所以這里利用了正則,結(jié)果值如圖2: this.initStyleReg = this.initColorCluster .join("|") .replace(/(/g, "(") // 括號的轉(zhuǎn)義 .replace(/)/g, ")") .replace(/0./g, "."); // 這里替換是因?yàn)槟J(rèn)的css中計(jì)算出來的值透明度會缺省0,所以索性就直接全部去掉0 } generateColors = primary => { return formula.map(f => { const value = f.exp.replace(/primary/g, primary); // 將字符串中的primary 關(guān)鍵字替換為實(shí)際值,以便下一步調(diào)用 `Color.convert` return Color.convert(value); // 生成一連串的顏色值,見下圖1,可以看見計(jì)算值全部變?yōu)榱薫rgb/rgba` 值 }); };
圖1:
圖2,黑色字即為顏色正則表達(dá)式:
好了,當(dāng)我們拿到了原始值之后,就可以開始進(jìn)行替換了,這里的替換源是什么?由于我們的網(wǎng)頁是通過如下 內(nèi)嵌style標(biāo)簽 的,所以替換原就是所有的style標(biāo)簽,而 element 是直接去請求網(wǎng)頁 打包好的的css文件:
注:并不是每次都需要查找所有的 style 標(biāo)簽,只需要一次,然后,后面的替換只要在前一次的替換而生成的 style 標(biāo)簽(使用so-ui-react-theme來做標(biāo)記)中做替換
下面是核心代碼:
changeTheme = color => { // 這里防止兩次替換顏色值相同,省的造成不必要的替換,同時(shí)驗(yàn)證顏色值的合法性 if (color !== this.state.themeColor && (ABBRRE.test(color) || HEXRE.test(color))) { const styles = document.querySelectorAll(".so-ui-react-theme").length > 0 ? Array.from(document.querySelectorAll(".so-ui-react-theme")) // 這里就是上說到的 : Array.from(document.querySelectorAll("style")).filter(style => { // 找到需要進(jìn)行替換的style標(biāo)簽 const text = style.innerText; const re = new RegExp(`${this.initStyleReg}`, "i"); return re.test(text); }); const oldColorCluster = this.initColorCluster.slice(); const re = new RegExp(`${this.initStyleReg}`, "ig"); // 老的顏色簇正則,全局替換,且不區(qū)分大小寫 this.clusterDeal(color); // 此時(shí) initColorCluster 已是新的顏色簇 styles.forEach(style => { const { innerText } = style; style.innerHTML = innerText.replace(re, match => { let index = oldColorCluster.indexOf(match.toLowerCase().replace(".", "0.")); if (index === -1) index = oldColorCluster.indexOf(match.toUpperCase().replace(".", "0.")); // 進(jìn)行替換 return this.initColorCluster[index].toLowerCase().replace(/0./g, "."); }); style.setAttribute("class", "so-ui-react-theme"); }); this.setState({ themeColor: color, }); } };
效果如下:
至此,我們的顏色值替換已經(jīng)完成了。正如官方所說,實(shí)現(xiàn)原理十分暴力
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102779.html
摘要:所以今天,就和大家一起聊一聊前端的安全那些事兒。我們就聊一聊前端工程師們需要注意的那些安全知識。殊不知,這不僅僅是違反了的標(biāo)準(zhǔn)而已,也同樣會被黑客所利用。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog... 隨著互聯(lián)網(wǎng)的發(fā)達(dá),各種WEB應(yīng)用也變得越來越復(fù)雜,滿足了用戶的各種需求...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼作為現(xiàn)代應(yīng)用,的大量使用,使得前端工程師們?nèi)粘5拈_發(fā)少不了拼裝模板,渲染模板。我們今天就來聊聊,拼裝與渲染模板的那些事兒。一改俱改,一板兩用。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼作為現(xiàn)代應(yīng)用,的大量使用,使得前端工程師們?nèi)粘5拈_發(fā)少不了拼裝模板,渲染模板。我們今天就來聊聊,拼裝與渲染模板的那些事兒。一改俱改,一板兩用。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼作為現(xiàn)代應(yīng)用,的大量使用,使得前端工程師們?nèi)粘5拈_發(fā)少不了拼裝模板,渲染模板。我們今天就來聊聊,拼裝與渲染模板的那些事兒。一改俱改,一板兩用。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/blog...
摘要:歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面不僅僅是代碼什么是功能統(tǒng)計(jì)作為一名開發(fā),我們的產(chǎn)品發(fā)布出去之后,無論是產(chǎn)品還是運(yùn)營,其實(shí)都是想及時(shí)了解產(chǎn)品對用戶產(chǎn)生的影響的。下一章,我們將繼續(xù)聊聊速度統(tǒng)計(jì)。 歡迎大家收看聊一聊系列,這一套系列文章,可以幫助前端工程師們了解前端的方方面面(不僅僅是代碼):https://segmentfault.com/bl...
閱讀 893·2021-11-15 11:38
閱讀 2526·2021-09-08 09:45
閱讀 2828·2021-09-04 16:48
閱讀 2574·2019-08-30 15:54
閱讀 941·2019-08-30 13:57
閱讀 1629·2019-08-29 15:39
閱讀 506·2019-08-29 12:46
閱讀 3530·2019-08-26 13:39