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 。
课程步骤
厨房水槽组件
在英语中(或许仅限美国英语),我们常说“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
如果你想跟着学习,可以下载第 9 课,和我一起学习更新内容。
git clone https://github.com/AJONPLLC/lesson-9-angular-material-router-outlet
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>
厨房水槽
在我们的厨房水槽示例中,我们用 `<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 主题功能包含在内,方法是将以下代码添加到任何主题文件的顶部。每个项目中只需添加一次此代码,因此,如果您的样式文件中使用各种功能更合适,mixins请include根据项目实际情况进行调整。
@import '~@angular/material/theming';
@include mat-core();
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
)
);
为我们的新调色板创建 Sass 变量,这里 mat-palette 函数返回颜色信息和映射并将其赋值给$angular-material-router-app-purple。
$angular-material-router-app-purple: mat-palette($md-purple);
新材质主题
有了新的调色板之后,就可以创建一个新的 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
);
请注意,我使用了默认的强调色和警告色,但您也可以将它们设置为任何您喜欢的颜色。这样,您在使用时就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);
}
...
侧边栏颜色选择器
现在我们已经定义了所有主题颜色,我们可以更新侧边栏按钮点击样式,使其包含正确的背景颜色,这将为每个菜单项分配颜色,并且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;
}
主题选择 Angular 服务
您可以阅读有关Angular 服务及其使用原因的资料。
创建颜色选择器服务
我喜欢把所有服务都创建在一个名为 `services` 的文件夹中core,我通常也会把其他东西,比如模型,也放在这个文件夹里。
ng g s core/services/color-picker.service
在这个服务中,我们需要在变量中为应用程序设置一个初始类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);
}
}
主题类更新应用程序
现在所有底层细节都已处理完毕,我们需要动态更新应用程序的类。这也是我喜欢将所有内容都放在模块中,与应用程序组件隔离开来的原因,因为这样可以让这部分代码看起来非常简洁。
只需添加即可将行为主题添加到整个应用程序[ngClass]="themeClass | async"。这将获取最后一个 Observable 字符串作为类名,并在服务广播时将其赋值给该对象。
app.component.html
<div [ngClass]="themeClass | async"><app-sidenav></app-sidenav></div>
当然,我们需要通过依赖注入来更新文件,从而告诉我们的 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 {}
}
萨斯混合料
对于那些不支持直接添加颜色指令的元素,最简单的添加颜色的方法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);
}
}
在每个主题类被选中时,都应包含 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);
}
在主题之外创建 Mixin
假设就我们这个简单的例子而言,我们希望所有按钮都以标准半径进行切换。
半径更圆润一些
我们可以添加一个新的 mixin 文件
angular-button-large-radius.scss
@mixin angular-button-large-radius {
.mat-raised-button,
.mat-stroked-button,
.mat-flat-button {
border-radius: 45px;
}
}
然后把这个也加到我们那包罗万象的俏皮话里去。
@import '../../../styles/angular-button-large-radius.scss';
@include angular-button-large-radius;

