赞
踩
AngularCli
主题切换首先要说明的是,这是基于Angualr
的一个主题切换策略,不附加任何UI
框架的主题系统.
经常使用Angular
的小伙伴应该都了解,Angular
和Scss
结合的很好,所以这篇Angular
的主题切换策略,也主要以Scss
预处理器来做。
我这里使用的AngualrCli
的版本为12
,还是比较新的。这篇策略可以向下兼容,这个不用担心。
这个策略在其他以scss
为预处理器的项目中好像也能实现。
由于不附加任何UI
框架的主题系统,所以要实现主题切换,那就需要我们手动的定义一套或多套主题颜色。
自定义一些颜色,是我们很常遇到的,因为设计图!=UI主题
,很现实的问题。
进入正题:
我们在angularcli
的项目中的新建一个styles
文件夹,然后在文件夹内新建三个文件:light.theme.scss
dark.theme.scss
theme.scss
light.theme.scss
:
$color: #fff;
dark.theme.scss
:
$color: #000;
因为是讲解嘛,所以,light
和dark
的主题样式,我就定义的。。。很简单。
传统的主题切换方案中,主要是通过js
来切换引入的主题来进行的,不同的主题引入不同的主题文件,从而适应不同的样式。
或者是,在css
中通过
:root {
--my-css: #fff;
}
.container {
color: var(--my-css)
}
这样的方式,定义变量,然后通过js
操纵css
的变量值来达到样式切换的操作。
但是,我要告诉各位的是,js
操纵scss
定义的变量值,这是行不通的,可以看这篇博客:如何在 Angular2 组件中操作 scss 变量
通过js
切换引入的主题文件,这个也是不好实现的。
所以,最关键的theme.scss
文件来了,这个文件,是对light.theme.scss
和dark.theme.scss
的整合,让我们可以只引入这一个文件,使用一套变量也能达到主题切换到效果。
theme.scss
:
@use './light.theme.scss' as light; @use './dark.theme.scss' as dark; // 定义变量 $themeColor:null; // 选择主题 // 根据参数定义变量值 @mixin choice($theme: null) { @debug $theme; @if $theme == light { $themeColor: light.$color !global; // !global 一定要加,因为$themeColor变量和当前赋值的变量是两个变量,且作用域不同,所以要通过global将变量改为全局变量,进而覆盖我们最外围定义的同名变量。 } @else if $theme == dark { $themeColor: dark.$color !global; } @else { $themeColor: light.$color !global; } }
看不懂这段代码的小伙伴,可要好好看看scss
的官网了,传送门:Sass
解释:
theme.scss
中引入了light
和dark
的主题文件mixin
里通过一段流程控制,来判断用户想使用那一套主题。theme
里定义的变量上。当然这么做的缺点是在theme
中我们会定义一堆变量,然后在流程控制里对这一堆变量分别赋值,可能会有点小麻烦。
这么做的好处是,如果以后我们又多增加了一套主题,比如:red
主题,那我们就只需要在mixin
里新加一个判断,然后引入red.theme.scss
定义的变量即可,非常方便。
接下来,我们就可以在我们的组件scss
文件中尝试使用了:
home.component.scss
:
@import "../../xxx/theme.scss"
@include choice(light);
.text {
color: $themeColor;
}
此时我们就可以看到效果了,通过切换choice
传入的值,我们就可以看到.text
类型所属的dom
呈现不同的颜色。
但是,还没完,这么做,我们只是纯手动实现了切换,距离理想的状态还有点距离,比如点击按钮就能自动帮我们切换主题色。
所以我由此延伸:
点击按钮切换主题 ——> 切换传入的变量
light | dark
——>切换主题.这么想是好的,但是行不通,我上面也写了,
js
没法操纵scss
,这是我找了好多方法,看了许多文章得出来的结果。
所以,如何切换是一个难题,不过,好在我从这篇博客上得到了启发:angular实现皮肤主题切换的方案
我先说一下原理:
我们都知道scss
最终经过编译后,还是要编译成css
的。
在css
中,我们也知道一中属性选择器[]
:
input[type="file"]{}
input[type="text"]{}
这两者虽然都是选择input
框,但是选择的确实不同类型的input
框。
所以,如果我们能让scss
在编译之后直接在组件样式文件中生成两套css
,然后这两套css
以属性选择器来区分,那我们是不是只需要切换属性,是不是就能达到样式切换到效果。
所以,根据理解,我们需要在index.html
的body
上添加一个自定义属性:
<body app-theme-style="light">
<app-root>Loading</app-root>
</body>
然后在theme.scss
中写一个mixin
,用来生成以属性区分的两套样式:
@mixin themeContainer() {
:host-context(body[app-theme-style="dark"]){
@include choice(dark); // 使用对应的主题
@content; // 用户自定义样式
}
:host-context(body[app-theme-style="light"]){
@include choice(light);
@content;
}
}
注意,这里的:host-context
必须要加,因为组件的scss
文件是从属于组件本身的,所以我们使用的body[app-theme-style="light"]
类名会导致组件无法匹配到对应的元素,而使用:host-context
伪类选择器可以让当前宿主元素从祖先节点中查找定义了该css
类的元素,直到文档根css
类。angular伪类选择器:host-context()
这也就解决了,我们在组件中找不到body
的问题。
OK,接下来,我们就不需要在组件的scss
中引入choice
的混入了,直接使用themeContainer
的混入。
home.component.scss
:
@import "../../xxx/theme.scss"
@include themeContainer() {
.text {
color: $themeColor;
}
}
此时,经过编译之后的home
的css
样式应该是这样的:
body[app-theme-style="light"] .text {
color: #fff;
}
body[app-theme-style="dark"] .text {
color: #000;
}
接下来,我们就只需要切换body
上自定定义属性的值就好了:
home.component.ts
changeTheme() {
const body = document.getElementsByTagName('body')[0]; // 获取dom
const theme = body.getAttribute('app-theme-style'); // 获取属性名
if(theme === 'light') {
this.themeNameBtn = theme; // 这是themeNameBtn是为了在页面上展示
body.setAttribute('app-theme-style','dark'); // 设置新的属性名
} else {
this.themeNameBtn = theme;
body.setAttribute('app-theme-style','light');
}
}
OK,到此为止,关于AngularCLi
主题切换的方法就讲解完毕了。
完结·撒花
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。