angular实现皮肤主题切换的方案

35次阅读

共计 2714 个字符,预计需要花费 7 分钟才能阅读完成。

分配了个给现有 angular6 项目实现主题切换的效果,实现 light dark 明暗两种显示效果,我使用 scss css 预处理器方便开发。效果(因为项目需要保密,只截了一点点):
切换样式 就是 实现 css 的 变化,主要思路是通过在 body 上控制自定义属性的值实现样式的控制,在 body 上 添加 data-theme-style=”dark”, 像这样:
<body data-theme-style=”dark”>
<app-root></app-root>
</body>
我们通过 data-theme-style 的值 来控制样式,本项目中 值有 light,dark 分别代表明暗两套主题
首先把切换主题 需要变化的 样式 抽离出来 定义成 mixin,如:背景颜色,字体颜色,阴影,边框等,,
这里以背景颜色 和 字体颜色举例:
@mixin mixin-bg-color($dark-color,$light-color){

[data-theme-style=”dark”] & {
background-color: $dark-color;
}
[data-theme-style=”light”] & {
background-color: $light-color;
}
}

@mixin mixin-font-color($dark-color,$light-color){

[data-theme-style=”dark”] & {
color: $dark-color;
}
[data-theme-style=”light”] & {
color: $light-color;
}
}
在需要使用相关样式的选择器里应用 mixin:
@include mixin-font-color(blue,red)
举个使用的例子,在 data-theme-style=dark 时 让 .title 类的字体颜色变为蓝色 在 data-theme-style=light 时 让 .title 类的字体颜色变为红色:
@mixin mixin-font-color($dark-color,$light-color){

[data-theme-style=”dark”] & {
color: $dark-color;
}
[data-theme-style=”light”] & {
color: $light-color;
}
}

.main{

.title{
@include mixin-font-color(blue,red)
}

}

上方的 scss 编译成 css 后的结果:
[data-theme-style=”dark”] .main .title {
color: blue; }
[data-theme-style=”light”] .main .title {
color: red; }
这样一看 就很明白了。接下来就是实现切换到效果,我这里是点击一个按钮 实现 dark 和 light 的来回切换,在 html 模板上:
<button class=”switch-theme-btn” (click)=”changeTheme()”>{{theme | uppercase}}</button>
ts 代码如下,注释很清楚了:
/**
* 使用 localStorage 存储主题的名称
* @param theme
*/
saveTheme(theme): void {
localStorage.setItem(`theme`, theme);
}

/**
* 获取主题名称并设置到 body
*/
getTheme(): void {
let theme = localStorage.getItem(`theme`); // 获取主题名称
if (!theme) {
theme = `dark`; // 本地存储中没有 theme 的话 使用 dark 主题
}
const body = document.getElementsByTagName(‘body’)[0];
body.setAttribute(‘data-theme-style’, theme); // 设置 data-theme-style 属性
this.theme = theme; // 用于界面显示
}
/**
* 点击按钮 触发改变主题的方法
*/
changeTheme(): void {
const body = document.getElementsByTagName(‘body’)[0];
if (body.getAttribute(`data-theme-style`) === ‘dark’) {
this.saveTheme(`light`); // 保存
this.getTheme(); // 更新获取
} else {
this.saveTheme(`dark`); // 保存
this.getTheme(); // 更新获取
}
}
在组件的 ngOnInit() 生命周期 调用下 this.getTheme() 初始化。。
做完这些 已经可以实现主题的切换了,但是 上方的 样式 写在公共的样式表里才有效,因为组件的样式只对对应的组件生效,使用 [data-theme-style=”dark”] 属性选择器无法匹配到对应的元素,该属性是定义在 body 上的,组件上肯定是没有的。如何在组件的样式里生效呢,这个问题困扰了我一阵子,还是在官网文档找到答案:
:host-context 选择器有时候,基于某些来自组件视图外部的条件应用样式是很有用的。例如,在文档的 元素上可能有一个用于表示样式主题 (theme) 的 CSS 类,你应当基于它来决定组件的样式。这时可以使用 :host-context() 伪类选择器。它也以类似 :host() 形式使用。它在当前组件宿主元素的祖先节点中查找 CSS 类,直到文档的根节点为止。在与其它选择器组合使用时,它非常有用。
参考:https://www.angular.cn/guide/…
根据文档的说明,我们把前面的 mixin 改一下:
@mixin mixin-font-color($dark-color,$light-color){

:host-context([data-theme-style=”dark”]) & {
color: $dark-color;
}
:host-context([data-theme-style=”light”]) & {
color: $light-color;
}
}

.main{

.title{
@include mixin-font-color(blue,red)
}

}

生成 的 css 是这样的:
:host-context([data-theme-style=”dark”]) .main .title {
color: blue; }
:host-context([data-theme-style=”light”]) .main .title {
color: red; }
至此 大功告成, 对你有帮助的话记得点个赞哦~~

正文完
 0