Angular集成web-ifc-viewer:IFC构件显隐功能异常问题
问题:Angular封装web-ifc-viewer实现IFC构件显隐功能失效
在Angular项目中封装web-ifc-viewer开发通用BIM浏览器,无法预先定义构件类别,执行toggleLayer方法后无任何效果,subset对象日志始终无变化,请求排查解决。
组件代码
import {AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild} from '@angular/core'; import {IfcViewerAPI} from "web-ifc-viewer"; import {Subject, takeUntil} from "rxjs"; import {AppThemes, BimViewerService} from "./bim-viewer.service"; @Component({ selector: 'almaviva-bim-viewer', templateUrl: './bim-viewer.template.html', styleUrls: ['./bim-viewer.component.scss'] }) export class BimViewerComponent implements AfterViewInit { @ViewChild('viewerContainer') container!: ElementRef; viewer?: IfcViewerAPI; model: any; ifcElement: any; loadingValue: number = 0; constructor() { } ngAfterViewInit(): void { if (this.container) { this.viewer = new IfcViewerAPI({container: this.container.nativeElement}); this.loadIfc('/assets/sample.ifc'); } } private async loadIfc(url: string) { try { if (this.viewer) { await this.viewer.IFC.loader.ifcManager.useWebWorkers(true, '/assets/IFCWorker.js'); await this.viewer.IFC.setWasmPath("wasm/"); this.viewer.axes.setAxes(1000); this.viewer.grid.setGrid(1000); await this.viewer.IFC.loader.ifcManager.applyWebIfcConfig({ USE_FAST_BOOLS: true, COORDINATE_TO_ORIGIN: true }); this.viewer.IFC.loader.ifcManager.setOnProgress( (event) => { this.loadingValue = Math.floor((event.loaded * 100) / event.total); } ) this.model = await this.viewer.IFC.loadIfcUrl(url); const project = await this.viewer.IFC.getSpatialStructure(this.model.modelID, true); this.ifcElement = project.children[0]; await this.viewer.shadowDropper.renderShadow(this.model.modelID); } } catch (e) { console.log(e); } } async toggleLayer(event: Event, layer: any) { const subset = this.viewer?.IFC.loader.ifcManager.createSubset({ modelID: this.model.modelID, ids: [layer.expressID], removePrevious: true, customID: `${layer.expressID}-custom-id` }); if (subset) { this.viewer?.context.getScene().remove(subset); } } }
HTML模板
<div> <mat-sidenav-container> <mat-sidenav mode="side" opened> <mat-toolbar> <span> BIM </span> </mat-toolbar> <mat-progress-bar mode="determinate" [value]="loadingValue"></mat-progress-bar> <mat-list role="list" *ngIf="!ifcElement"> <mat-list-item role="listitem"> Caricamento IFC in corso... </mat-list-item> </mat-list> <mat-accordion *ngIf="ifcElement"> <mat-expansion-panel *ngFor="let arch of ifcElement?.children || []"> <mat-expansion-panel-header> <mat-panel-title> {{arch.Name?.value || arch.LongName?.value || 'Architettura'}} </mat-panel-title> </mat-expansion-panel-header> <mat-list role="list"> <mat-list-item role="listitem" *ngFor="let layer of arch.children"> <mat-checkbox (click)="toggleLayer($event, layer)"> {{layer.Name?.value || layer.LongName?.value || 'N/A'}} </mat-checkbox> </mat-list-item> </mat-list> </mat-expansion-panel> </mat-accordion> </mat-sidenav> <mat-sidenav-content> <div id="viewer-container" #viewerContainer></div> <div class="loading-spinner-wrapper" *ngIf="loadingValue!==100"> <mat-spinner mode="indeterminate" diameter="35"></mat-spinner> </div> </mat-sidenav-content> </mat-sidenav-container> </div>
问题排查与修复
核心问题分析
- 逻辑倒置:当前代码每次调用
createSubset后立即将子集从场景移除,等于完全没有创建有效可控制的子集。 - 目标错误:传入的
layer是IFC空间结构元素(如楼层、区域),这类元素本身无几何图形,仅传它的expressID创建子集不会生成任何可见内容。 - 无状态保存:未保留已创建子集的引用,无法后续切换可见性。
修复方案
1. 添加子集状态存储
在组件类中新增Map保存已创建的子集引用:
private subsets = new Map<string, THREE.Group>();
2. 重写toggleLayer方法
async toggleLayer(event: Event, layer: any) { // 阻止事件冒泡,避免触发父级折叠面板的展开/折叠 event.stopPropagation(); const subsetId = `${layer.expressID}-custom-id`; let subset = this.subsets.get(subsetId); if (subset) { // 切换现有子集的可见性 subset.visible = !subset.visible; return; } // 递归获取当前空间节点下所有带几何的构件ID const getAllElementIds = (node: any): number[] => { let ids: number[] = []; // 排除纯空间结构元素,只收集实际构件 const spatialTypes = ['IFCPROJECT', 'IFCSITE', 'IFCBUILDING', 'IFCBUILDINGSTOREY']; if (!spatialTypes.includes(node.type)) { ids.push(node.expressID); } if (node.children) { node.children.forEach((child: any) => { ids = ids.concat(getAllElementIds(child)); }); } return ids; }; const allIds = getAllElementIds(layer); if (!allIds || allIds.length === 0) return; // 创建子集并添加到场景 subset = this.viewer?.IFC.loader.ifcManager.createSubset({ modelID: this.model.modelID, ids: allIds, removePrevious: false, customID: subsetId }); if (subset) { this.subsets.set(subsetId, subset); // 初始可见状态可根据需求调整 // subset.visible = false; } }
3. 补充说明
- 必须递归遍历空间结构节点,收集所有子构件的
expressID,才能创建包含可见几何的子集。 - 阻止事件冒泡是为了避免点击复选框时触发父级
mat-expansion-panel的展开/折叠行为,导致交互冲突。 - 用Map存储子集引用,确保每次切换时直接操作已存在的子集,无需重复创建,提升性能。
内容的提问来源于stack exchange,提问作者Ludovico Dellavecchia
相关产品推荐
相关产品推荐

