乐趣区

关于前端:Angular-Material-主题系统三多主题切换

上一篇介绍了如何自定义 Material 主题,当初来看看怎么定义多个主题,并在运行时动静切换。
能够采纳官网介绍的类名包裹形式,或者咱们也能够事后编译,按需引入。话不多说,let’s rock the code!????

按类名切换明暗格调

如上篇所言,咱们首先就来实现怎么切换网站的暗黑主题。
留神✨:能够在 styles.scss 中调用 angular-material-theme mixin 屡次,来生成多套不同的主题,但 @mat-core 始终只能调用一次。

创立两个主题

咱们先创立两个主题,一个亮堂格调的主题作为默认,另一个暗黑格调的主题放在 .unicorn-dark-theme 这个类名中。这样一来,任何在该类名内的元素,都会利用暗黑格调的主题款式。

@import '~@angular/material/theming';
@include mat-core();

// 定义一个自定义色彩配置 ( 和前一篇一样)$candy-app-primary: mat-palette($mat-indigo);
$candy-app-accent:  mat-palette($mat-pink, A200, A100, A400);
$candy-app-theme:   mat-light-theme((
  color: (
    primary: $candy-app-primary,
    accent: $candy-app-accent,
  )
));

// 蕴含默认主题色彩以及文字排版
@include angular-material-theme($candy-app-theme);

// 定义另一个暗黑格调的主题
$dark-theme:   mat-dark-theme((
  color: (
    primary: $candy-app-primary,
    accent: $candy-app-accent,
  )
));

// 用一个类名包裹暗黑主题款式
.unicorn-dark-theme {@include angular-material-color($dark-theme);
}

留神✨:暗黑主题采纳 angular-material-color,而不是默认主题应用的 angular-material-theme,因为后者不光会生成主题色彩的款式,还会生成其余款式。这里只须要扭转色彩即可。而如果须要独自扭转文字排版,能够应用 angular-material-typography。相似的,细粒度的自定义某组件也是同理(例如:mat-button-theme / mat-button-color / mat-button-typography)。

额定解决基于覆盖层的组件

因为基于覆盖层的组件(例如:menu、select、dialog 等)不是被应用程序的根组件包裹,而是和根组件平级的 <div class="cdk-overlay-container"> 节点。因而,要让它们也能依据类名切换主题款式,须要做一点额定的解决:

import {OverlayContainer} from '@angular/cdk/overlay';

@Component({// ...})
export class AppComponent {
  // 用以绑定类名
  isDarkMode = false;

  constructor(private overlayContainer: OverlayContainer) {}

  private processOverlayBaseComponentTheme(checked: boolean) {
    // 获取这个 div 元素
    const overlayContainerElement = this.overlayContainer.getContainerElement()
    const themeWrapperClassName = 'unicorn-dark-theme'
    if (checked) {overlayContainerElement.classList.add(themeWrapperClassName);
    } else {overlayContainerElement.classList.remove(themeWrapperClassName);
    }
  }
}

动静切换类名

通过 Angular 类名绑定,来切换不同的主题:

<div [class.unicorn-dark-theme]="isDarkMode">
  <!-- 受主题切换影响的元素和组件 -->
</div>

当初,就能够在两个主题之间自在切换了!????

看看成果:

多整几个

尽管下面咱们实现了动静主题切换,不过只有暗黑 / 亮堂的两个主题重复横跳显得不太够看。所以,当初到了整活儿工夫,一次性整它十个色彩!

创立多个主题

相比创立两个主题时手动去创立,创立一个多个主题的 styles.scss,咱们就利用 Sass 的循环来实现,次要分为几步:

  1. 自定义主题的结尾操作都一样,先引入 mat-core,并先定义一个默认主题:

    @import '~@angular/material/theming';        
    @include mat-core();
    
    // 默认主题色彩配置
    $candy-app-primary: mat-palette($mat-indigo);
    $candy-app-accent:  mat-palette($mat-pink, A200, A100, A400);
    $candy-app-theme:   mat-light-theme((
      color: (
        primary: $candy-app-primary,
        accent: $candy-app-accent,
      )
    ));
    
    // 生成默认主题款式 (色彩,文字排版,间距)
    @include angular-material-theme($candy-app-theme);
    @include app-component-color($candy-app-theme);
    @include theme-test-color($candy-app-theme);
  2. 创立一个 map 对象,在这个 map 中定义各个主题配置。每个主题配置的 key 就是这个主题的 class 类名:

    $app-themes: (indigo-pink : (primary-base: $mat-indigo, accent-base: $mat-pink),
      deeppurple-amber: (primary-base: $mat-deep-purple, accent-base: $mat-amber),
      blue-yellow : (primary-base: $mat-blue, accent-base: $mat-yellow),
      pink-bluegrey : (primary-base: $mat-pink, accent-base: $mat-blue-gray,),
      purple-green : (primary-base: $mat-purple, accent-base: $mat-green),
    );
  3. 通过 @each 循环,创立明暗个五个主题色彩:

    @each $css-class, $theme in $app-themes {$primary: mat-palette(map-get($theme, primary-base))
      $accent: mat-palette(map-get($theme, accent-base))
      
      // key 值作为类名
      .#{$css-class} {
        $light-theme: mat-dark-theme((
          color: (
            primary: $primary,
            accent: $accent,
          )
        ));
        // 生成色彩款式
        @include angular-material-color($light-theme);
        // 增加两个业务组件测试主题适配
        @include app-component-color($light-theme);
        @include theme-test-color($light-theme);
      }
      
      // key 值作为类名
      .#{$css-class}-dark {
        $dark-theme: mat-dark-theme((
          color: (
            primary: $primary,
            accent: $accent,
          )
        ));
        // 生成色彩款式
        @include angular-material-color($dark-theme);
        // 增加两个业务组件测试主题适配
        @include app-component-color($dark-theme);
        @include theme-test-color($dark-theme);
      }
    }

动静切换主题

通过扭转 root 元素的 class,来动静切换域名:

<div [class]="currentTheme">
  <!-- 受主题切换影响的元素和组件 -->
</div>
themelist = [
  "amber-lime",
  "amber-lime-dark",
  "deeppurple-amber",
  "deeppurple-amber-dark",
  "blue-yellow",
  "blue-yellow-dark",
  "pink-bluegrey",
  "pink-bluegrey-dark",
  "purple-green",
  "purple-green-dark"
]

switchTheme(theme: string) {if (this.currentTheme)
    this.overlayContainerEle.classList.remove(this.currentTheme);
  this.currentTheme = theme
  this.overlayContainerEle.classList.add(this.currentTheme);
}

ok,当初咱们有了 10 套主题!

不过能够看到,当初编译打包后的 styles.css 和 styles.js,未压缩的状况下,快高达 1MB 了(每个主题会减少 css 文件 50kb 大小)。所以,咱们试着懒加载它们。

懒加载动静切换

当初咱们曾经能够动静的切换主题了,可只有两三个主题还好,如果我的应用程序,要提供很多的主题搭配,让用户自在选用呢,styles.scss 一次蕴含这么多份主题款式,切实有损加载性能(每个主题减少 50kb)。
遇到这种状况,咱们能够事后编译好须要用到主题,在运行时动静切换它们,就能够按需加载了。

  1. 首先在 src > theme 文件夹中创立几个主题文件,例如这个 green-amber-dark.scss

    @import "~@angular/material/theming";
    
    @import "../app/app.component.theme.scss";
    @import "../app/component/theme-test/theme-test.component.theme";
    
    $green-primary: mat-palette($mat-green);
    $amber-accent: mat-palette($mat-amber);
    
    $green-amber-dark-theme: mat-dark-theme((
      color: (
        primary: $green-primary,
        accent: $amber-accent
      )
    ));
    
    @include angular-material-theme($green-amber-dark-theme);
    
    // 增加两个业务组件测试主题适配
    @include app-component-color($green-amber-dark-theme);
    @include theme-test-color($green-amber-dark-theme);
  2. 而后,在 angular.json > projects > architect > build > styles 中,新增刚刚创立的主题文件,inject 设置为 false,意味着它只会被打包为独自的 css 文件,但不会被引入我的项目中:

    "styles": [
      "src/styles.scss",
      {
        "input": "src/theme/green-amber-theme.scss",
        "inject": false
      }
    ],
  3. 最初,通过给 <link> 动静赋值即可:

    switchTheme(theme: string): void {
     const id = 'lazy-load-theme';
     const link = document.getElementById(id);
     // 第一次切换,新建一个 link 元素
     if (!link) {const linkEl = document.createElement('link');
       linkEl.setAttribute('rel', 'stylesheet');
       linkEl.setAttribute('type', 'text/css');
       linkEl.setAttribute('id', id);
       linkEl.setAttribute('href', `${theme}-theme.css`);
       document.head.appendChild(linkEl);
     } else {
       // 替换 link 元素的 href 地址 
       (link as HTMLLinkElement).href = `${theme}-theme.css`;
     }
    }

这样就实现了主题文件的懒加载,不过须要留神的是,默认的主题文件仍旧须要在 styles.scss 中援用,它也懒加载的话,会导致首次加载时页面闪动。

小结

本篇介绍了如何动静切换主题,渐进的包含了三种状况:

  1. 网站只须要在亮堂和暗黑模式下切换主题,那么只须要通过一个独特的类名包裹主题即可,切换时只是切换类名即可
  2. 网站若是须要预设多个主题,能够利用 Sass 的 @each 办法,来批量生成,再依据类名动静切换。
  3. 如果网站的主题量很大或者可预感的会一直减少,又或者想要尽量多的优化加载性能,能够采纳动静加载主题文件的形式,动静的管制一个 link 标签即可。

本篇的三种状况,能够参照 Github 示例,按 branch 切换

成果能够参考 成果展现,点击右上角工具栏能够切换各个主题。

有时候我的项目里可不止用上了 Material 的组件库,适配第三方组件库的主题,不像本人的业务组件,能够随便更改,就要另辟蹊径了,对于 Material 主题零碎的工程实际,咱们下一篇见!????

退出移动版