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

分配了个给现有angular6项目实现主题切换的效果,实现light dark明暗两种显示效果,我使用scss css预处理器方便开发。
效果(只截了一点点):
图片描述css

切换样式 就是 实现css 的 变化,主要思路是经过在body 上控制自定义属性的值实现样式的控制,在body上 添加 data-theme-style=”dark”, 像这样:html

<body data-theme-style="dark">
  <app-root></app-root>
</body>

咱们经过data-theme-style的值 来控制样式,本项目中 值有 light,dark 分别表明明暗两套主题app

首先把切换主题 须要变化的 样式 抽离出来 定义成mixin,如:背景颜色,字体颜色,阴影,边框等,,ide

这里以背景颜色 和 字体颜色举例:字体

@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:ui

@include mixin-font-color(blue,red)

举个使用的例子,在data-theme-style=dark时 让 .title 类的字体颜色变为蓝色 在data-theme-style=light时 让 .title 类的字体颜色变为红色:this

@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后的结果:spa

[data-theme-style="dark"] .main .title {
  color: blue; }
[data-theme-style="light"] .main .title {
  color: red; }

这样一看 就很明白了。
接下来就是实现切换到效果,我这里是点击一个按钮 实现 dark 和light 的来回切换,
在html模板上:code

<button class="switch-theme-btn" (click)="changeTheme()">{{theme | uppercase}}</button>

ts 代码以下,注释很清楚了:component

/**
   * 使用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; }

至此 大功告成

原文地址:https://www.crazyming.com/note/705/

相关文章
相关标签/搜索