CSS换肤方案
概述
页面效果跟随 CSS 样式表的不同定义,实现皮肤效果切换。
原理
CSS StyleSheet
在 HTML 结构中,实时插入<style>
或者 <link>
标签,引入 CSS 资源,均可以影响页面渲染。
皮肤样式的基础
实现皮肤的切换,最根本的一点,是保证 HTML 结构不变,CSS className
不变。
通过相同的 CSS Selector
,定义同一块区域的样式,完成样式替换。
举个例子:
<article class="article">
<p class="title">标题</p>
<div class="content">
<p>内容</p>
</div>
</article>
<style title="normal">
.article{
padding: 4px;
}
.title{
font-size: 18px;
font-weight: bold;
color: blue;
}
.content{
font-size: 12px;
color: #fff;
}
</style>
替换 style[title=normal]
的样式,需要保证作用于相同HTML节点 CSS选择器 的优先级,要大于等于已生效的样式优先级。
所以,一般都会保持 CSS选择器 的一致性。
比如以下用于替换的 style[title=special]
样式
<style title="special">
.article{
padding: 8px;
}
.title{
font-size: 20px;
font-weight: bold;
color: green;
}
.content{
font-size: 14px;
color: #333;
}
</style>
样式如何生效与失效
生效样式
插入<style>
或者 <link>
标签到 HTML 中,标签携带的内容,浏览器会进行读取,并渲染到页面上。
失效样式
- 使用 BOM(浏览器对象模型)API 读取到
StyleSheet
,设置disabled
属性为true
- 从 HTML 结构中,删除
<style>
或者<link>
标签 <link>
标签的rel
属性,设置为alternate
,或者media
属性设置为print
如何生成样式资源
本质上,样式资源就是一份 CSS 静态资源文件
接下来,会介绍几种生成样式资源的方式。
手工生成
人工手工编写 CSS,然后分成几个文件,供页面引入。
theme-noraml.css
theme-yellow.css
theme-blue.css
配合前文的样式生效与失效章节提供的方式,即可完成皮肤切换。
CSS变量
将 CSS 样式中的属性值,抽离出来,变成 CSS 变量。
再通过设置 CSS 变量的取值,实时切换皮肤效果。
/* 根,用于设置CSS变量 */
:root{
--primary-color: #666;
}
div{
color: var(--primary-clor);
}
通过 document.body.style.setProperty
,可以实时修改 CSS变量 的取值。
document.body.style.setProperty('--primary-clor', 'blue');
通过Less、Sass等CSS扩展语言
Less 和 Sass 都支持在浏览器端实时编译样式,并输出 CSS。
下面,以 Less 为例,进行分解:
在浏览器环境中引入 Less 和 模板Less :
<link rel="stylesheet/less" type="text/css" href="styles.less" />
<script src="[https://cdn.jsdelivr.net/npm/less@4](https://cdn.jsdelivr.net/npm/less@4)" ></script>
然后设置 Less 变量:
less.modifyVars({
'@buttonFace': '#5B83AD',
'@buttonText': '#D9EEF2'
});
Less 在接收到变量后,会自动重新编译,达到实时换肤的效果。
使用模板引擎
利用模板引擎的插值语法,对 CSS 文件中的属性值,进行替换
下面,以模板引擎 ejs 为例进行讲解:
生成模板
// template.txt
.article{
padding: <%= articlePadding %>;
}
.title{
font-size: <%= titleFontSize %>;
font-weight: <%= titleFontWeight %>;
color: <%= titleColor %>;
}
.content{
font-size: <%= contentFontSize %>;
color: <%= contentColor %>;
}
皮肤配置
// normal.json
{
"articlePadding": "4px",
"titleFontSize": "18px",
"titleFontWeight": "bold",
"titleColor": "#666",
"contentFontSize": "12px",
"contentColor": "#fff",
}
根据皮肤配置,产出CSS资源
import template from 'template.txt';
import normal from 'normal.json';
// 产出CSS
const normalCss = ejs.render(template, normal);
// 插入HTML
const head = document.head || document.getElementsByTagName('head')[0];
const style = document.createElement('style');
head.appendChild(style);
style.type = 'text/css';
if (style.styleSheet){
style.styleSheet.cssText = normalCss;
} else {
style.appendChild(document.createTextNode(css));
}