发布于 2026-01-06 2 阅读
0

Angular Material 主题化 Angular Material 主题化 Kitchen Sink 组件 Angular Flex-Layout Angular Material 主题创建 主题选择 Angular 服务 Sass Mixins 工具

Angular Material 主题

Angular Material 主题

厨房水槽组件

Angular Flex布局

Angular Material 主题创建

主题选择 Angular 服务

萨斯混合料

工具

原文链接:https://ajonp.com/courses/angularmaterial/angular-material-theming/

这节课/视频我做得有点赶,所以如果你有任何问题,请到Slack 频道提问。

Angular Material 主题

🌎 演示:https://ajonp-lesson-10.firebaseapp.com/kitchensink

Angular Material 是官方支持的组件,用于在 Angular 中实现 Google 的 Material Design 系统。Angular
Material 包含 4 个预置主题。

  • 深紫琥珀色
  • 靛蓝色
  • 粉蓝灰色
  • 紫绿色 我们将研究如何创建自己的主题,并将其包含在我们的默认样式中,然后使用它为我们的每个组件样式提供 mixin。

我听到的关于 Angular Material 主题的最大抱怨可能就是它需要使用 Sass 函数构建一套全新的 CSS,这会导致 style.css 文件变得臃肿。可以参考Ionic CSS Variables来了解一下它是如何实现这一点的。截至撰写本文时,这个问题在 GitHub 上仍然是一个未解决的 issue 。

课程步骤

  1. 厨房水槽组件
  2. Angular Flex布局
  3. Angular Material 主题创建
  4. 主题选择 Angular 服务
  5. 萨斯混合料
  6. 工具

厨房水槽组件

在英语中(或许仅限美国英语),我们常说“Everything but the kitchen sink”(除了厨房水槽之外的一切),而在编程中,我们经常用“Kitchensink”(厨房水槽)来指代网站上所有可能用到的东西。在我们的示例中,我们将使用Angular Material 的 Kitchensink主题,以便突出显示所有可能的组件以及该主题对它们的作用。

这些组件可以在material.angular.io/components上找到,其中一些也来自组件开发工具包 (CDK)。

克隆仓库

通常我会提供一份指南来指导你完成整个过程(如果你想跟着做,我在视频中也展示了这一点),但为了简化操作,我建议你直接克隆第 10 课的仓库(或者 fork 它并克隆你自己的仓库),然后查看它以进行本次演示。

git clone https://github.com/AJONPLLC/lesson-10-angular-material-theming
Enter fullscreen mode Exit fullscreen mode

如果你想跟着学习,可以下载第 9 课,和我一起学习更新内容。

git clone https://github.com/AJONPLLC/lesson-9-angular-material-router-outlet
Enter fullscreen mode Exit fullscreen mode

Angular Flex布局

Angular 的 FlexLayout(或 fxLayout)提供了一种绝佳的替代方案,无需手动编写所有 FlexLayout 的 CSS 代码。它与Bootstrap 的 Flex非常相似,但我感觉它的功能更加强大。

这是 tburleson 举的一个很好的例子:

工具栏

在我们的示例中,我们使用了nogrow工具栏中的选项,以便使用 使 Material 图标保持在右侧偏移fxFlex="nogrow"

侧边栏组件.html

<mat-sidenav-container>
  <mat-sidenav opened="false" mode="over" #snav>
    <mat-nav-list>
      <mat-list-item>
        <h4
          matLine
          routerLink="/welcome"
          [routerLinkActiveOptions]="{ exact: true }"
          routerLinkActive="active-link"
        >
          Home
        </h4>
      </mat-list-item>
      <mat-list-item>
        <h4
          matLine
          routerLink="/kitchensink"
          [routerLinkActiveOptions]="{ exact: true }"
          routerLinkActive="active-link"
        >
          Kitchen Sink
        </h4>
      </mat-list-item>
      <mat-list-item>
        <h4 matLine routerLink="/books" routerLinkActive="active-link">
          Books
        </h4>
      </mat-list-item>
    </mat-nav-list>
  </mat-sidenav>
  <mat-sidenav-content>
    <mat-toolbar color="primary">
      <button
        type="button"
        aria-label="Toggle sidenav"
        mat-icon-button
        (click)="snavToggle(snav)"
      >
        <mat-icon>menu</mat-icon>
      </button>
      <span fxFlex>
        <a class="home-link" routerLink=".">{{ title }}</a>
      </span>
      <span fxFlex="nogrow">
        <button mat-icon-button [matMenuTriggerFor]="menu">
          <mat-icon>format_color_fill</mat-icon>
        </button>
        <mat-menu #menu="matMenu">
          <button mat-menu-item (click)="pickColor('')">
            <div class="color-swatch color-swatch-primary" mat-menu-item></div>
          </button>
          <button mat-menu-item (click)="pickColor('green')">
            <div class="color-swatch color-swatch-green" mat-menu-item></div>
          </button>
          <button mat-menu-item (click)="pickColor('pink')">
            <div class="color-swatch color-swatch-pink" mat-menu-item></div>
          </button>
          <button mat-menu-item (click)="pickColor('purple')">
            <div class="color-swatch color-swatch-purple" mat-menu-item></div>
          </button>
          <button mat-menu-item (click)="pickColor('yellow')">
            <div class="color-swatch color-swatch-yellow" mat-menu-item></div>
          </button>
          <button mat-menu-item (click)="pickColor('warn')">
            <div class="color-swatch color-swatch-warn" mat-menu-item></div>
          </button>
        </mat-menu>
      </span>
    </mat-toolbar>
    <router-outlet></router-outlet>
  </mat-sidenav-content>
</mat-sidenav-container>
Enter fullscreen mode Exit fullscreen mode

厨房水槽

在我们的厨房水槽示例中,我们用 `<span>` 标签包裹了每个组件<span fxFlex>,然后将整个组件包裹在一个 `<div>` 标签中,<div style="max-width: 80%" fxLayout="column" fxLayoutAlign="center center">这样可以确保所有元素居中,并且每个元素都位于一个 flexbox 布局中。您可以看到,每个 `<span>` 标签在 HTML 渲染时fxFlex都会应用相应的样式。flex: 1 1 1e-09px;box-sizing: border-box;

Angular Material 主题创建

您可以使用 @angular/material 包中自带的 4 个预置主题,只需将它们导入到您的 style.css 文件中即可。

@import "~@angular/material/prebuilt-themes/indigo-pink.css";
@import "~@angular/material/prebuilt-themes/deeppurple-amber.css";
@import "~@angular/material/prebuilt-themes/pink-bluegrey.css";
@import "~@angular/material/prebuilt-themes/purple-green.css";

自定义主题

我更倾向于直接添加,这样您就可以创建自己的品牌颜色。如果您不让您的品牌标识展现出来,您就无法创建一个独一无二的网站(您懂的,营销人员!)。

我找到的最好的网站是Material Design Palette Generator ,与Google 的 Material Color Tool不同,DPG 允许您直接导出到 sass 变量,这正是我们在创建 Angular Material 主题时所需要的。

选择下载(向下箭头)时,请确保选择 ANGULAR JS 2(MATERIAL 2)。
下载设计调色板生成器

更新主题文件

然后,您可以使用所有这些自定义变量来创建新主题。Sass 变量应定义调色板中每种颜色的完整光谱及其对比度。

请确保将所有 Material Sass 主题功能包含在内,方法是将以下代码添加到任何主题文件的顶部。每个项目中只需添加一次此代码,因此,如果您的样式文件中使用各种功能更合适,mixinsinclude根据项目实际情况进行调整。

@import '~@angular/material/theming';
@include mat-core();
Enter fullscreen mode Exit fullscreen mode

SASS 变量

angular-material-router-theme.scss

$md-purple: (
  50: #ffffff,
  100: #d3cbe7,
  200: #b2a5d5,
  300: #8974be,
  400: #775fb4,
  500: #674ea7,
  600: #5a4492,
  700: #4d3b7d,
  800: #403168,
  900: #342754,
  A100: #f9f7fd,
  A200: #b7a2ec,
  A400: #7c5dcb,
  A700: #7256b9,
  contrast: (
    50: #000000,
    100: #000000,
    200: #000000,
    300: #000000,
    400: #ffffff,
    500: #ffffff,
    600: #ffffff,
    700: #ffffff,
    800: #ffffff,
    900: #ffffff,
    A100: #000000,
    A200: #000000,
    A400: #ffffff,
    A700: #ffffff
  )
);
Enter fullscreen mode Exit fullscreen mode

为我们的新调色板创建 Sass 变量,这里 mat-palette 函数返回颜色信息和映射并将其赋值给$angular-material-router-app-purple

$angular-material-router-app-purple: mat-palette($md-purple);
Enter fullscreen mode Exit fullscreen mode

新材质主题

有了新的调色板之后,就可以创建一个新的 Sass 变量$angular-material-router-app-theme-purple来存放完整的主题信息了。

$angular-material-router-app-theme-purple: mat-dark-theme(
  $angular-material-router-app-purple,
  $angular-material-router-app-accent,
  $angular-material-router-app-warn
);
Enter fullscreen mode Exit fullscreen mode

请注意,我使用了默认的强调色和警告色,但您也可以将它们设置为任何您喜欢的颜色。这样,您在使用时就color="warn"可以用紫色或灰色代替红色,只需在任何主题中更改变量即可。

样式类更新

目前我们只创建了一堆 Sass 变量,它们尚未被应用中包含。为了实现这一点,我们需要将它们包含在一个外部类中。这可以在一个style.scss文件中完成,该文件应该由 Angular CLI 生成,或者styles如果您克隆了仓库,它已经存在于文件夹中。

导入 SCSS 主题文件

首先,请确保通过导入 scss 文件来包含包含我们所有变量的新主题文件@import 'angular-material-router-theme.scss';

你可以在任何组件中这样做来使用 Sass 变量。

将主题包含到类变量中

现在我们将把应用程序的整个主题(css)包含在这个类中。

...
.angular-material-router-app-theme-purple {
  $theme: $angular-material-router-app-theme-purple;
  @include angular-material-theme($theme);
}
...
Enter fullscreen mode Exit fullscreen mode

侧边栏颜色选择器

现在我们已经定义了所有主题颜色,我们可以更新侧边栏按钮点击样式,使其包含正确的背景颜色,这将为每个菜单项分配颜色,并且border-radius50% 的透明度使其成为圆形。

侧边栏组件.scss


.color-swatch {
  position: relative;
  width: 36px;
  height: 36px;
  margin: 6px;
  border-radius: 50%;
  overflow: hidden;
}

.color-swatch-primary {
  background-color: mat-color($angular-material-router-app-primary, 500);
}
.color-swatch-primary:hover {
  background-color: mat-color(
    $angular-material-router-app-primary,
    500
  ) !important;
}
.color-swatch-green {
  background-color: mat-color($angular-material-router-app-green, 500);
}
.color-swatch-green:hover {
  background-color: mat-color(
    $angular-material-router-app-green,
    500
  ) !important;
}
.color-swatch-pink {
  background-color: mat-color($angular-material-router-app-pink, 500);
}
.color-swatch-pink:hover {
  background-color: mat-color(
    $angular-material-router-app-pink,
    500
  ) !important;
}
.color-swatch-purple {
  background-color: mat-color($angular-material-router-app-purple, 500);
}
.color-swatch-purple:hover {
  background-color: mat-color(
    $angular-material-router-app-purple,
    500
  ) !important;
}
.color-swatch-yellow {
  background-color: mat-color($angular-material-router-app-yellow, 500);
}
.color-swatch-yellow:hover {
  background-color: mat-color(
    $angular-material-router-app-yellow,
    500
  ) !important;
}
.color-swatch-warn {
  background-color: mat-color($angular-material-router-app-warn, 500);
}
.color-swatch-warn:hover {
  background-color: mat-color(
    $angular-material-router-app-warn,
    500
  ) !important;
}
Enter fullscreen mode Exit fullscreen mode

主题选择 Angular 服务

您可以阅读有关Angular 服务及其使用原因的资料。

创建颜色选择器服务

我喜欢把所有服务都创建在一个名为 `services` 的文件夹中core,我通常也会把其他东西,比如模型,也放在这个文件夹里。

ng g s core/services/color-picker.service
Enter fullscreen mode Exit fullscreen mode

在这个服务中,我们需要在变量中为应用程序设置一个初始类initialClass。如果您观看教学视频,就会看到它是如何与浏览器中的本地存储配合使用的。这样,用户就可以设置主题,而无需在每次刷新时都进行选择。所以我们首先查找本地存储,如果找不到,就添加初始类,该类也已设置在我们的变量中BehaviorSubject。您还会注意到我们正在更新一个名为 ` <div class="div">` 的内容OverlayContainer,它来自Material CDK,允许许多浮动面板拥有样式。

color-picker.service.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { OverlayContainer } from '@angular/cdk/overlay';

@Injectable({
  providedIn: 'root'
})
export class ColorPickerService {
  initialClass = 'angular-material-router-app-theme';
  colorClass$: BehaviorSubject<string> = new BehaviorSubject(this.initialClass);
  constructor(private overlayContainer: OverlayContainer) {
    const storageClass = localStorage.getItem('color-picker');
    console.log(storageClass);
    if (storageClass) {
      overlayContainer.getContainerElement().classList.add(storageClass);
      this.colorClass$.next(storageClass);
    } else {
      overlayContainer.getContainerElement().classList.add(this.initialClass);
    }
  }
  getColorClass() {
    return this.colorClass$;
  }
  setColorClass(className: string) {
    this.overlayContainer.getContainerElement().classList.forEach(css => {
      this.overlayContainer.getContainerElement().classList.remove(css);
    });
    this.overlayContainer.getContainerElement().classList.add(className);
    this.colorClass$.next(className);
    localStorage.setItem('color-picker', className);
  }
}
Enter fullscreen mode Exit fullscreen mode

主题类更新应用程序

现在所有底层细节都已处理完毕,我们需要动态更新应用程序的类。这也是我喜欢将所有内容都放在模块中,与应用程序组件隔离开来的原因,因为这样可以让这部分代码看起来非常简洁。

只需添加即可将行为主题添加到整个应用程序[ngClass]="themeClass | async"。这将获取最后一个 Observable 字符串作为类名,并在服务广播时将其赋值给该对象。

app.component.html

<div [ngClass]="themeClass | async"><app-sidenav></app-sidenav></div>
Enter fullscreen mode Exit fullscreen mode

当然,我们需要通过依赖注入来更新文件,从而告诉我们的 html 文件这是从哪里来的,app.component.ts并添加我们的新内容color-service

app.component.ts

import { ColorPickerService } from './core/services/color-picker.service';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = 'angular-material-router-outlet';
  themeClass;
  constructor(private colorPicker: ColorPickerService) {
    this.themeClass = this.colorPicker.getColorClass();
  }
  ngOnInit(): void {}
}
Enter fullscreen mode Exit fullscreen mode

萨斯混合料

对于那些不支持直接添加颜色指令的元素,最简单的添加颜色的方法color是创建一个基于当前主题颜色的 mixin。例如,我们的厨房水槽元素本身mat-grid-list不提供颜色,但我们可以通过提供一个 mixin 来为背景添加颜色。

将 Mixin 添加到主题

angular-material-router-theme.scss

@mixin mat-grid-mixin($theme) {
  $primary: map-get(
    $map: $theme,
    $key: primary
  );

  mat-grid-tile {
    background-color: mat-color($primary, 500);
    color: mat-contrast($primary, 500);
  }
}
Enter fullscreen mode Exit fullscreen mode

在每个主题类被选中时,都应包含 mixin@include mat-grid-mixin($theme);更新。现在,所有使用 mat-grid-tile 的元素都将获得当前主主题颜色的背景色。您可以从 Sass 函数中获取任何所需的键,并在 ` and`函数map-get中使用它mat-colormat-contrast

.angular-material-router-app-theme-purple {
  $theme: $angular-material-router-app-theme-purple;
  @include angular-material-theme($theme);
  @include mat-grid-mixin($theme);
}
Enter fullscreen mode Exit fullscreen mode

在主题之外创建 Mixin

假设就我​​们这个简单的例子而言,我们希望所有按钮都以标准半径进行切换。

普通半径按钮

半径更圆润一些

半径 45 像素

我们可以添加一个新的 mixin 文件

angular-button-large-radius.scss

@mixin angular-button-large-radius {
  .mat-raised-button,
  .mat-stroked-button,
  .mat-flat-button {
    border-radius: 45px;
  }
}
Enter fullscreen mode Exit fullscreen mode

然后把这个也加到我们那包罗万象的俏皮话里去。

@import '../../../styles/angular-button-large-radius.scss';

@include angular-button-large-radius;
Enter fullscreen mode Exit fullscreen mode

工具

课程中使用的工具

其他工具

文章来源:https://dev.to/codingcatdev/angular-material-theming-32km