Angular+ol-cesium 3D可视化操作时出现闪烁问题求助
Ol-Cesium + Angular 3D视图操作闪烁问题
环境版本
- Angular v19
- olcs 2.22.1
- Cesium 1.123
- ol 10.3.1
问题描述
3D视图下执行任意操作都会触发视图闪烁,2D视图无异常。涉及操作包括:移除并添加矢量图层、修改要素坐标、更新要素样式等。
应用结构简单:主组件(app.component.ts)通过按钮触发指令组件(cesium.directive.ts)中的方法,所有逻辑均在指令组件内。切换到3D视图后点击移动按钮,图标移动时视图会明显闪烁。
组件代码示例
主组件 app.component.ts
import { Component, ViewChildren, QueryList, AfterViewInit} from '@angular/core'; import { CesiumDirective } from './cesium.directive'; @Component({ selector: 'app-root', template: ` <div class="map-container"> <div id="map" appCesium #cesiumDirective ></div> <button class="toggle-view-button" (click)="toggleView()">Toggle 2D/3D View</button> <button class="move-button" (click)="moveButton()">move</button> </div> `, styles: [` .map-container { width: 100%; height: 100vh; position: relative; } #map { width: 100%; height: 100%; position: absolute; top: 0; left: 0; } .toggle-view-button { position: absolute; top: 10px; right: 10px; z-index: 1000; } .move-button { position: absolute; top: 90px; right: 10px; z-index: 1000; } `], standalone: false }) export class AppComponent implements AfterViewInit { @ViewChildren(CesiumDirective) cesiumDirectives!: QueryList<CesiumDirective>; private cesiumDirective!: CesiumDirective; ngAfterViewInit() { setTimeout(() => { this.cesiumDirective = this.cesiumDirectives.first; console.log('Cesium Directive found:', this.cesiumDirective); }); } moveButton() { if(this.cesiumDirective) { this.cesiumDirective.moveButton(); } } }
指令组件 cesium.directive.ts
import { Directive, ElementRef, OnInit } from '@angular/core'; import OLCesium from 'olcs'; import Map from 'ol/Map'; import { Feature, View } from 'ol'; import TileLayer from 'ol/layer/Tile'; import { OSM } from 'ol/source'; import { Point } from 'ol/geom'; import {Fill, Icon, Stroke, Style, Text} from 'ol/style'; import VectorSource from 'ol/source/Vector'; import VectorLayer from 'ol/layer/Vector'; @Directive({ selector: '[appCesium]', standalone: false, }) export class CesiumDirective implements OnInit { private map!: Map; private ol3d: any; private vectorLayer!: VectorLayer; private is3DEnabled: boolean = false; private icon1Feature!: any; constructor(private el: ElementRef) { } ngOnInit(): void { this.initMap(); this.addFeature(); } private addFeature(): void { // 创建带图标的要素 this.icon1Feature = new Feature({ geometry: new Point([700000, 200000, 10000]) }); this.icon1Feature.setStyle(new Style({ image: new Icon({ anchor: [0.5, 1], src: 'assets/icon.png' }), text: new Text({ text: 'Icon with anchor on the bottom center', stroke: new Stroke({ color: 'black', width: 3 }), fill: new Fill({ color: 'white' }) }) })); // 创建矢量源和图层 const vectorSource = new VectorSource({ features: [this.icon1Feature] }); this.vectorLayer = new VectorLayer({ source: vectorSource }); this.map.addLayer(this.vectorLayer); } private initMap(): void { try { this.map = new Map({ target: this.el.nativeElement, layers: [ new TileLayer({ visible: true, preload: Infinity, source: new OSM(), }) ], view: new View({ zoom: 5, center: [6000000, 4000000] }) }); this.ol3d = new OLCesium({ map: this.map, target: this.el.nativeElement, }); const scene = this.ol3d.getCesiumScene(); scene.debugShowFramesPerSecond = true; this.ol3d.setEnabled(false); } catch (error) { console.error('Error initializing map:', error); } } public toggleView(): void { try { this.is3DEnabled = !this.is3DEnabled; if (this.ol3d) { this.ol3d.setEnabled(this.is3DEnabled); } } catch (error) { console.error('Error in toggleView:', error); } } public moveButton() { const currentCoordinates = this.icon1Feature.getGeometry().getCoordinates(); const deltaX = 100; const deltaY = 200; const newCoordinates = [currentCoordinates[0] + deltaX, currentCoordinates[1] + deltaY, currentCoordinates[2]]; this.icon1Feature.setGeometry(new Point(newCoordinates)); } }
解决方案
1. 优化OL要素更新方式
避免每次创建新的Point对象,直接修改已有几何坐标,减少对象创建带来的同步开销:
public moveButton() { const geometry = this.icon1Feature.getGeometry() as Point; const currentCoordinates = geometry.getCoordinates(); const deltaX = 100; const deltaY = 200; const newCoordinates = [currentCoordinates[0] + deltaX, currentCoordinates[1] + deltaY, currentCoordinates[2]]; geometry.setCoordinates(newCoordinates); }
2. 手动控制Cesium同步
使用OL-Cesium的FeatureOverlay手动同步单个要素,替代默认的全量更新:
public moveButton() { const geometry = this.icon1Feature.getGeometry() as Point; const currentCoordinates = geometry.getCoordinates(); const deltaX = 100; const deltaY = 200; const newCoordinates = [currentCoordinates[0] + deltaX, currentCoordinates[1] + deltaY, currentCoordinates[2]]; geometry.setCoordinates(newCoordinates); // 手动同步到Cesium const featureOverlay = this.ol3d.getFeatureOverlay(this.vectorLayer); featureOverlay.updateFeature(this.icon1Feature); }
3. 隔离Angular变更检测
将Cesium相关操作放到NgZone外执行,避免Angular变更检测触发不必要的重绘:
import { NgZone } from '@angular/core'; // 构造函数注入NgZone constructor(private el: ElementRef, private ngZone: NgZone) {} public moveButton() { this.ngZone.runOutsideAngular(() => { const geometry = this.icon1Feature.getGeometry() as Point; const currentCoordinates = geometry.getCoordinates(); const deltaX = 100; const deltaY = 200; const newCoordinates = [currentCoordinates[0] + deltaX, currentCoordinates[1] + deltaY, currentCoordinates[2]]; geometry.setCoordinates(newCoordinates); const featureOverlay = this.ol3d.getFeatureOverlay(this.vectorLayer); featureOverlay.updateFeature(this.icon1Feature); }); }
4. 优化Cesium渲染设置
关闭调试选项,启用按需渲染和抗锯齿,减少闪烁感:
private initMap(): void { try { // ...已有地图初始化代码 const scene = this.ol3d.getCesiumScene(); scene.debugShowFramesPerSecond = false; // 关闭FPS显示 scene.fxaa = true; // 开启FXAA抗锯齿 scene.requestRenderMode = true; // 启用按需渲染 scene.maximumRenderTimeChange = 0.0; this.ol3d.setEnabled(false); } catch (error) { console.error('Error initializing map:', error); } }
整合优化后的完整moveButton方法
public moveButton() { this.ngZone.runOutsideAngular(() => { const geometry = this.icon1Feature.getGeometry() as Point; const currentCoordinates = geometry.getCoordinates(); const deltaX = 100; const deltaY = 200; const newCoordinates = [currentCoordinates[0] + deltaX, currentCoordinates[1] + deltaY, currentCoordinates[2]]; geometry.setCoordinates(newCoordinates); const featureOverlay = this.ol3d.getFeatureOverlay(this.vectorLayer); featureOverlay.updateFeature(this.icon1Feature); }); }
内容的提问来源于stack exchange,提问作者coman
相关产品推荐
相关产品推荐

