Angular 17中Azure Maps动画标记无法移动的问题求助
问题:Azure Maps路径动画中箭头标记静止不动
复现Azure Maps路径动画示例时,相机动画表现正常,但箭头图标保持静止。已确认Atlas库及atlas.animations加载正常,无控制台库相关错误,使用最新官方动画库(已将azmaps替换为atlas)。
相关代码
HTML代码
<div #myMap style="position:relative;width:100%;min-width:290px;height:600px;"></div> <div style="position:absolute;top:15px;left:15px;border-radius:5px;padding:5px;background-color:white;"> <button (click)="play()">Play</button> <button (click)="pause()">Pause</button> <button (click)="stop()">Stop</button> <button (click)="reset()">Reset</button> <br/><br/> Follow: <input type="checkbox" (click)="toggleFollow()" title="Follow" checked/><br/> Follow offset: <input type="checkbox" (click)="toggleFollowOffset()" title="Follow offset"/><br/> Loop: <input type="checkbox" (click)="toggleLooping()" title="Loop"/><br/> Reverse: <input type="checkbox" (click)="toggleReverse()" title="Reverse"/> </div> <fieldset style="width:calc(100% - 30px);min-width:290px;margin-top:10px;"> <legend>Animate along a route path</legend> This sample shows how to smoothly animate a symbol along a route path taking into consideration timestamps for each point in the route path. This sample also includes controls and options for the animation. This sample uses the open source Azure Maps Animation module </fieldset>
TypeScript代码(Angular组件)
import { Component, ElementRef, ViewChild } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import atlas from 'azure-maps-control'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet], templateUrl: './app.component.html', styleUrl: './app.component.scss', }) export class AppComponent { @ViewChild('myMap', { static: true }) myMap!: ElementRef; map!: atlas.Map; pin!: atlas.Shape; lineSource!: atlas.source.DataSource; pinSource!: atlas.source.DataSource; animation!: any; routePoints = [ new atlas.data.Feature(new atlas.data.Point([-122.34758, 47.62155]), { _timestamp: new Date('Tue, 18 Aug 2020 00:53:53 GMT').getTime() }), new atlas.data.Feature(new atlas.data.Point([-122.34764, 47.61859]), { _timestamp: new Date('Tue, 18 Aug 2020 00:54:53 GMT').getTime() }), new atlas.data.Feature(new atlas.data.Point([-122.33787, 47.61295]), { _timestamp: new Date('Tue, 18 Aug 2020 00:56:53 GMT').getTime() }), new atlas.data.Feature(new atlas.data.Point([-122.34217, 47.60964]), { _timestamp: new Date('Tue, 18 Aug 2020 00:59:53 GMT').getTime() }) ]; constructor() { } ngOnInit() { this.initializeMap(); } initializeMap() { this.map = new atlas.Map(this.myMap.nativeElement, { center: [-122.345, 47.615], zoom: 14, view: 'Auto', authOptions: { authType: atlas.AuthenticationType.subscriptionKey, subscriptionKey: 'API_KEY', }, }); this.map.events.add('ready', () => this.onMapReady()); } onMapReady() { this.map.imageSprite.createFromTemplate('arrow-icon', 'marker-arrow', 'teal', '#fff').then(() => { this.initializeSourcesAndLayers(); this.initializePin(); this.initializeAnimation(); }); } initializeSourcesAndLayers() { this.lineSource = new atlas.source.DataSource(); this.pinSource = new atlas.source.DataSource(); this.map.sources.add([this.lineSource, this.pinSource]); const path = this.routePoints.map(f => f.geometry.coordinates); this.lineSource.add(new atlas.data.LineString(path)); this.map.layers.add(new atlas.layer.LineLayer(this.lineSource, null, { strokeColor: 'DodgerBlue', strokeWidth: 3 })); this.map.layers.add(new atlas.layer.SymbolLayer(this.pinSource, null, { iconOptions: { image: 'arrow-icon', anchor: 'center', rotation: ['+', 180, ['get', 'heading']], rotationAlignment: 'map', ignorePlacement: true, allowOverlap: true }, textOptions: { ignorePlacement: true, allowOverlap: true } })); } initializePin() { this.pin = new atlas.Shape(this.routePoints[0]); this.pinSource.add(this.pin); } initializeAnimation() { this.animation = (window as any).atlas.animations.moveAlongRoute(this.routePoints, this.pin, { timestampProperty: 'timestamp', captureMetadata: true, loop: false, reverse: false, rotationOffset: 0, speedMultiplier: 60, map: this.map, zoom: 15, pitch: 45, rotate: true }); } toggleAnimationOption(option: string, value?: any) { const options = this.animation.getOptions(); this.animation.setOptions({ [option]: value !== undefined ? value : !options[option] }); } play() { this.animation.play(); } pause() { this.animation.pause(); } stop() { this.animation.stop(); } reset() { this.animation.reset(); } toggleFollow() { this.toggleAnimationOption('map', this.map); } toggleFollowOffset() { this.toggleAnimationOption('rotationOffset', this.animation.getOptions().rotationOffset === 0 ? 90 : 0);} toggleLooping() { this.toggleAnimationOption('loop'); } toggleReverse() { this.toggleAnimationOption('reverse'); } }
问题排查与修复方案
核心问题1:时间戳属性名不匹配
你的routePoints中时间戳属性是_timestamp,但动画初始化时timestampProperty配置的是timestamp,导致动画模块无法读取时间戳计算位置,箭头标记无法移动。
修复:
修改initializeAnimation中的timestampProperty值为_timestamp:
this.animation = (window as any).atlas.animations.moveAlongRoute(this.routePoints, this.pin, { timestampProperty: '_timestamp', // 匹配routePoints中的属性名 // 其他配置保持不变 });
核心问题2:toggleFollow方法逻辑错误
当前toggleFollow方法强制将map选项设为实例,无法取消跟随状态,导致后续切换失效。
修复:
修改toggleFollow方法,实现map选项的切换:
toggleFollow() { const currentOptions = this.animation.getOptions(); this.toggleAnimationOption('map', currentOptions.map ? null : this.map); }
验证与额外建议
- 动画模块会自动更新
atlas.Shape的坐标和heading属性,SymbolLayer中通过['get', 'heading']读取旋转角度的配置是正确的,无需修改。 - 可添加日志确认动画模块加载状态,避免加载顺序问题:
initializeAnimation() { console.log('Animation module loaded:', !!((window as any).atlas?.animations)); // 动画初始化代码 }
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

