上一篇介绍了如何自定义 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 主题零碎的工程实际,咱们下一篇见!????