如何通过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
相关产品推荐
相关产品推荐

