You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Angular指令按需切换Google Material Symbol的填充状态

问题

我开发了一个自定义的npm字体图标库,现在需要在Angular指令中实现:当用户编写 <div icon="search fill"> 时,系统识别fill关键词,将图标从轮廓样式切换为当前颜色填充样式。

目前我通过index.html引入Google Material Symbols样式表:

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,1,0" />    

其中FILL参数为预定义值。我已编写包含其他功能的IconDirective指令代码:

import {Directive, ElementRef, Input, Renderer2} from '@angular/core';

@Directive({
  selector: '[icon]',
  standalone: true,
})
export class IconDirective {
  private _iconName: string = '';
  private _spin: boolean = false;
  private _size: string | undefined;
  private _flipHorizontal: boolean = false;
  private _flipVertical: boolean = false;
  private _color: string | undefined;
  private _fill: boolean = false;
  private _rotate: number = 0;

  @Input('icon')
  set icon(value: string) {
    const parts = value.split(' ');
    this._iconName = parts[0];

    console.log(this._iconName, value)

    this._spin = parts.includes('spin');

    this._flipHorizontal = parts.includes('flip-horizontal');

    this._flipVertical = parts.includes('flip-vertical');

    this._size = parts.find(part => part.match(/^[0-5]x$/)) || undefined; // Default to 1x

    const colorPart = parts.find(part => part.startsWith('color-'));
    this._color = colorPart ? colorPart.split('-')[1] : undefined;

    this._rotate = parseInt(parts.find(part => part.startsWith('rotate-'))?.split('-')[1] || '0');

    this.updateIcon();
  }

  constructor(private el: ElementRef, private renderer: Renderer2) {
  }

  ngAfterViewInit(): void {
    this.setupLazyLoading();
  }

  private setupLazyLoading(): void {
    const options = {
      root: null, // observes changes in the viewport
      threshold: 0.01, // trigger when 1% of the element is visible
    };

    const observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          this.updateIcon();
          observer.unobserve(entry.target); // Stop observing once loaded
        }
      });
    }, options);

    observer.observe(this.el.nativeElement);
  }

  private updateIcon(): void {
    // Clear previous classes
    this.el.nativeElement.className = '';

    if (!this._iconName) {
      return;
    }

    console.log('Setting icon');
    this.renderer.setProperty(this.el.nativeElement, 'textContent', this._iconName);
    this.renderer.addClass(this.el.nativeElement, 'material-symbols-outlined');

    // Apply size, spin, and flip classes
    if (this._spin) {
      this.renderer.addClass(this.el.nativeElement, 'spin');
    }
    if (this._flipHorizontal) {
      this.renderer.addClass(this.el.nativeElement, 'flip-horizontal');
    }
    if (this._flipVertical) {
      this.renderer.addClass(this.el.nativeElement, 'flip-vertical');
    }
    if (this._size) {
      this.renderer.addClass(this.el.nativeElement, `icon-${this._size}`);
    }
    if (this._color) {
      this.renderer.setStyle(this.el.nativeElement, 'color', this._color);
    }
    if (this._rotate) {
      this.renderer.setStyle(this.el.nativeElement, 'transform', `rotate(${this._rotate}deg)`); // Rotate icon
    }
  }
}

我试过类似spin和flip的实现方式,也修改过CSS设置.[icon] { fill: currentColor },但均未成功,请问该如何实现这一填充功能?

解决方案

要实现fill关键词的填充功能,需结合Material Symbols的字体变体特性和Angular指令逻辑修改,具体步骤如下:

1. 识别fill关键词

在icon输入属性的setter中,添加对fill关键词的判断,将状态赋值给_fill变量:

@Input('icon')
set icon(value: string) {
  const parts = value.split(' ');
  this._iconName = parts[0];

  // ... 保留原有其他判断逻辑

  // 新增fill关键词识别
  this._fill = parts.includes('fill');

  this.updateIcon();
}

2. 控制填充状态(两种可选方式)

Material Symbols的填充效果由font-variation-settings中的FILL参数控制,而非普通CSS的fill属性,因此需要针对该属性做处理:

方式一:直接设置字体变体样式

在updateIcon方法中,根据_fill的值动态设置font-variation-settings:

private updateIcon(): void {
  // ... 保留原有清除类、设置图标名称和基础类的逻辑

  // 处理fill状态
  if (this._fill) {
    this.renderer.setStyle(
      this.el.nativeElement,
      'font-variation-settings',
      '"FILL" 1, "wght" 400, "GRAD" 0, "opsz" 24'
    );
  } else {
    // 恢复默认轮廓样式
    this.renderer.setStyle(
      this.el.nativeElement,
      'font-variation-settings',
      '"FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24'
    );
  }

  // ... 保留原有其他样式、类添加逻辑
}

方式二:通过自定义类控制(更易维护)

先在全局CSS中定义两个控制类:

.material-symbols-outlined.fill {
  font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 24;
}
.material-symbols-outlined.outline {
  font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
}

然后在updateIcon方法中根据_fill状态添加/移除对应类:

private updateIcon(): void {
  // ... 保留原有清除类、设置图标名称和基础类的逻辑

  // 处理fill状态
  if (this._fill) {
    this.renderer.addClass(this.el.nativeElement, 'fill');
  } else {
    this.renderer.addClass(this.el.nativeElement, 'outline');
  }

  // ... 保留原有其他样式、类添加逻辑
}

3. 关键注意事项

  • 不要使用普通的fill: currentColor,因为Material Symbols是字体图标,填充效果由字体变体参数控制,而非SVG的fill属性。
  • 确保引入的样式表包含FILL参数的可选范围(你当前的链接已包含FILL@1,0,支持填充和轮廓两种状态)。

内容的提问来源于stack exchange,提问作者swdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:34:55