Angular Material 18+ 高级教程 – Material Icon

前言

不熟悉 Icon 的可以先看这篇 CSS – Icon

Material Design 有自己的一套 Icon 设计。Angular Material 默认就使用这一套。

不过呢,目前 v17.2.0 用的是旧版本的的设计 -- Material Icons

Material Design 3 推出了新版本的设计 -- Material Symbols

本篇会教如何使用旧的 Material Icons,新的 Material Symbols,还有 SVG 版 Icon。

 

Angular Material for Material Icons (旧设计)

要在 Angular 项目中使用 Material Icons 并不一定要搭配 Angular Material。

Without Angular Material 

首先在 index.html 添加 link 

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

App Template

<span class="material-icons">home</span>

App Styles

.material-icons {
  font-size: 56px;
  color: red;
}

效果

Material Icons 是用 font-family 实现的,所以它只需要 CSS Styles 就可以 working 了。

不仅仅是 Material Icons,其它 Icon (e.g. fontawesomeFlaticonionicons) 用法也大同小异。

Do we really need Angualr Material Icon?

Material Icons, fontawesome, Flaticon, ionicons 它们的使用方式已经很简单了,我们还有必要引入 Angualr Material Icon 吗?

不一定,有 3 种情况下使用 Angualr Material Icon 会比较好:

  1. multiple icon library

    一个 icon library 未必可以满足整个项目的需求,比如说我们选了 Material Icons 作为 primary,但有些 icon 或许它没用。

    这时就需要额外加一些 SVG icon 弥补上。

    一旦出现 2 个 library,代码风格管理可能就不统一了,比如 Material Icons 用 <span>,fontawesome 用 <i>,有些又是 <svg>。

    这时用 Angualr Material 组件 <mat-icon> 就可以把它们统一起来。

  2. multiple projects

    不同项目 icon 风格可能会不一样。哪怕它们都使用 Material Symbols 也可能分 Outlined, Rounded, Sharp 不同变种。

    这种情况使用 Angualr Material 组件 <mat-icon> 也可以达到统一管理的目的。

  3. 顺风水

    人家 wrap 一层给你一定是有原因的嘛。

With Angular Material Icon

App 组件 import MatIconModule

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { MatIconModule } from '@angular/material/icon';

@Component({
  selector: 'app-root',
  standalone: true,
  // 1. import MatIconModule
  imports: [MatIconModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {}

App Template

<mat-icon aria-hidden="false" aria-label="home icon" fontIcon="home"></mat-icon>

如果项目不需要支持 screen reader 可以去掉所有 "aria-" attribute。

下面 2 种写法几乎是等价的,唯一的区别好像是 search engine 会以为第一种是 text。

<mat-icon>home</mat-icon>
<mat-icon fontIcon="home" />

Angular Material 推荐我们使用第二种,但是 Google Fonts Icon 给的例子却是第一种,我也不懂要听谁的😕。

CSS Styles

mat-icon {
  color: red;
  font-size: 56px;
  width: 56px;
  height: 56px;
}

修改 size 比较麻烦,需要改 3 个属性 font-size, width, height。

这是因为 <mat-icon> 是 inline-block 

inline-block 的作用是在 font-family 加载完成前先卡着位置,这样布局就不会跳一下。

rendered html

<mat-icon _ngcontent-ng-c2283532919="" role="img" class="mat-icon notranslate material-icons mat-ligature-font mat-icon-no-color" aria-hidden="true" data-mat-icon-type="font">home</mat-icon>

<mat-icon> 组件其实也没用替我们做多少事儿,主要就是添加了 class "material-icons"。其它的像 role, aria-hidden 这些是 for accessibility。

 

Angular Material for Material Symbols (新设计)

Material Symbols 是 Material Design 3 对 Icon 的新设计。

官网依旧是 https://fonts.google.com/icons

它有一个 select options 可以切换回去旧设计。

旧设计有 5 个变种,新设计只有 3 个变种

我选 Rounded 作为例子。

Setup Angular Material

index.html 添加 link 和 styles (去官网拿)

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<style>
  .material-symbols-rounded {
    font-variation-settings:
    'FILL' 0,
    'wght' 400,
    'GRAD' 0,
    'opsz' 24
  }
</style>

Material Symbols 的使用代码长这样

<span class="material-symbols-rounded">home</span>

它和 Material Icons 的区别是 class 不一样。

这时我们需要设置 Angular Material Icon 让它知道我们要换掉原本的 class。

App 组件

export class AppComponent {
  constructor() {
    const iconRegistry = inject(MatIconRegistry);
    iconRegistry.setDefaultFontSetClass('material-symbols-rounded');
  }
}

MatIconRegistry 是一个 Root Level Provider。通过它我们可以对全局的 <mat-icon> 进行设置。

提醒:这个设置要趁早哦,因为 MatIcon 是在 OnInit 阶段去读取 MatIconRegistry 的,

而且它不会监听 MatIconRegistry 变更,如果错过了 OnInit 只有当 @Input fontIcon 变更才会再读取 MatIconRegistry 了。

个人建议大家可以在 App 组件 constructor 或者 APP_INITIALIZER 阶段去设置 MatIconRegistry。

App Template

<mat-icon>home</mat-icon>

这里和 Material Icons 是一模一样的,我们只需要设置 MatIconRegistry 就可以了,它会负责修改所有 <mat-icon> 的逻辑。

效果

注意:如果使用 fontIcon attribute 的话

<mat-icon fontIcon="home" />

MatIconRegistry 还需要添加多一个 class "mat-ligature-font" 才行。

const iconRegistry = inject(MatIconRegistry);
iconRegistry.setDefaultFontSetClass('material-symbols-rounded', 'mat-ligature-font');

相关文档 Docs – MatIcon

class "mat-ligature-font" 是输出伪元素的 selector,所以使用 fontIcon 的写法就必须搭配它。

Use both Material Symbols and Material Icons

如果我们想同时使用 2 种不同的 Icon Library 也是可以的。

export class AppComponent {
  constructor() {
    const iconRegistry = inject(MatIconRegistry);
    iconRegistry.registerFontClassAlias('symbols', 'material-symbols-rounded mat-ligature-font');
  }
}

上一 part 我们用 MatIconRegistry.setDefaultFontSetClass 替换了默认的设置,

这里我们改用 registerFontClassAlias 添加一个设置。

使用方式

<!-- 用默认的 Material Icons -->
<mat-icon fontIcon="home" />

<!-- 用新的 Material Symbols -->
<mat-icon fontIcon="home" fontSet="symbols" />

通过 fontset="name" 来表明用哪一个,这个 name 就是上面 registerFontClassAlias 的注册的名字。

效果

Fill mode

Fill mode 就是有填满颜色,如上图所示。

我们在 index.html 的 styles 里头有一个 'FILL'

<style>
  .material-symbols-rounded {
    font-variation-settings:
    'FILL' 0,
    'wght' 400,
    'GRAD' 0,
    'opsz' 24
  }
</style>

它就是用来配置全局 fill mode 的,0 代表 false 不要 fill,1 代表 true 要 fill。

想要针对某一个 icon 做修改也可以

mat-icon.fill {
  font-variation-settings: 'FILL' 1;
}

直接 override 就是了。

 

题外话:Self Hosting for Material Symbols

这个和 Angular Material Icon 无关,我只是顺便提一下。

上面 index.html 引入的 link,它的具体内容是

我们下载它的 .woff2 放到自己的 server,然后把 style 代码写进 style.scss 就可以了。

 

Angular Material SVG Icon

假设有 2 个 SVG icons 

我们想使用 <mat-icon> 来统一输出它们。

addSvgIcon

App Template

<mat-icon svgIcon="home" />
<mat-icon svgIcon="toggle_on" />

把原本的 fontIcon attribute 改成 svgIcon attribute。

然后用 MatIconRegistry 做设置

export class AppComponent {
  constructor() {
    const iconRegistry = inject(MatIconRegistry);
    const domSanitizer = inject(DomSanitizer);
    // 1. 需要 bypass 消毒
    const homeSvgUrl = domSanitizer.bypassSecurityTrustResourceUrl('/assets/icons/home.svg');
    const toggleOnSvgUrl = domSanitizer.bypassSecurityTrustResourceUrl('/assets/icons/toggle_on.svg');

    // 2. 一个一个添加
    iconRegistry.addSvgIcon('home', homeSvgUrl);
    iconRegistry.addSvgIcon('toggle_on', toggleOnSvgUrl);
  }
}

不熟悉消毒的朋友可以看这篇:DomSanitizer

效果

它会用 HttpClient 发请求去下载 SVG file。

提醒:我们需要 provide HttpClient 哦。

addSvgIconInNamespace

SVG file 通常会来自四面八方不同的设计,多了就很乱,Angular Material Icon 提供了一个 namespace 概念帮助管理。

iconRegistry.addSvgIconInNamespace('my-design', 'home', homeSvgUrl);
iconRegistry.addSvgIconInNamespace('my-design', 'toggle_on', toggleOnSvgUrl);

把 addSvgIcon 换成 addSvgIconInNamespace,并且提供一个 namespace 名字

使用方式

<mat-icon svgIcon="my-design:home" />
<mat-icon svgIcon="my-design:toggle_on" />

多了一个 "my-design:" prefix。

addSvgIconLiteral or addSvgIconLiteralInNamespace

除了可以提供 SVG URL,我们也可以直接提供 SVG element。(提醒:一样要消毒)

const homeSvgHtml = domSanitizer.bypassSecurityTrustHtml(`
    <svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>
`);
iconRegistry.addSvgIconLiteralInNamespace('my-design', 'home', homeSvgHtml);

这样它就不需要 HttpClient 发请求去下载了。

addSvgIconSet or addSvgIconSetInNamespace

SVG 有一个概念叫 sprite,就是把多个 SVG combine 在一个 SVG file 里,这样就一次性下载多个 SVG。

我们把 home.svg 和 toggle_on.svg combine 成 1 个 sprite.symbol.svg。

使用 addSvgIconSet or addSvgIconSetInNamespace 做设置

const svgSpriteUrl = domSanitizer.bypassSecurityTrustResourceUrl(
  '/assets/icons/svg-icon-sprite/symbol/svg/sprite.symbol.svg',
);
iconRegistry.addSvgIconSetInNamespace('my-design', svgSpriteUrl);

效果

2 个 SVG icons 只需要下载一个 sprite svg file。

提醒:Angular Material Icon 并不知道 sprite svg file 的具体内容,它会一次性下载所有 sprite svg file。

 

多出的 4px

Template

<div class="container">
  <mat-icon fontIcon="home" />
</div>

Styles

.container {
  background-color: pink;
  
  mat-icon {
    background-color: lightblue;
  }
}

效果

icon 下方粉色的部分就是多出来的 4px。

原因是 <mat-icon> 是 display: inline-block

vertical-align: baseline 情况下就会出现 4px,更多原理可以看这篇

如果不希望出现这个 4px,可以给 <mat-icon> 设置 display: block,或者 vertical-align: bottom。

mat-icon {
  background-color: lightblue;
  vertical-align: bottom;
  /* display: block; */
}

效果

 

总结

本篇介绍了如何使用 Angular Material Icon 做 Icon 统一管理。

不管是不同的 Icon Library (Material 新 vs Material 旧)

或者不同输出方式 (font-family vs SVG)

Angular Material Icon 都统一了 <mat-icon> 的接口。大部分情况我们只需要对全局的 MatIconRegistry 做设置管理就可以了。 

  

 

目录

上一篇 Angular Material 18+ 高级教程 – CDK Scrolling

下一篇 Angular Material 18+ 高级教程 – Material Ripple

想查看目录,请移步 Angular 18+ 高级教程 – 目录

喜欢请点推荐👍,若发现教程内容以新版脱节请评论通知我。happy coding 😊💻

 

posted @ 2024-03-05 20:20  兴杰  阅读(415)  评论(0编辑  收藏  举报