Angular路由动画偶现旧内容闪烁问题求助
Angular路由动画旧内容闪烁问题修复
我在简易Angular应用中集成路由切换动画,前3次动画表现符合预期,但第4次路由切换时出现旧内容闪烁的异常现象。
现有动画代码
export const slideInAnimation = trigger('routeAnimations', [ transition('watchList => movieDetail', [ style({ position: 'relative' }), query(':enter, :leave', [ style({ position: 'absolute', top: 0, right: 0, width: '100%' }) ]), query(':enter', [style({ right: '-100%', opacity: 0, position: 'fixed' })]), group([ query(':leave', [animate('1s ease-out', style({ right: '100%', opacity: 0 }))]), query(':enter', [animate('1s ease-out', style({ right: '0%', opacity: 1, }))]) ]), ]), transition('movieDetail => watchList', [ style({ position: 'relative' }), query(':enter, :leave', [ style({ position: 'absolute', top: 0, left: 0, width: '100%' }) ]), query(':enter', [style({ left: '-100%', opacity: 0 })]), query(':leave', [style({ position: 'fixed' })]), group([ query(':leave', [animate('1s ease-out', style({ left: '100%', opacity: 0 }))]), query(':enter', [animate('1s ease-out', style({ left: '0%', opacity: 1 }))]) ]), ]) ]);
使用position: fixed的原因
为了在路由导航时保留之前的scrollTop值,同时确保红色的movieDetail路由动画始终显示在视口内(不设置fixed的话,动画会定位在元素顶部,用户滚动后显示异常),滚动恢复逻辑如下:
if (data instanceof NavigationEnd) { this.mainContent.scrollTo({ top: this.scrollService.scrollTopSubject$.getValue() }) }
问题分析与修复方案
核心原因
闪烁问题大概率是因为动画结束后,:leave元素的fixed定位没有被正确重置,导致后续路由切换时旧元素的渲染状态异常;另外,滚动恢复时机和动画执行时序冲突,触发了不必要的重绘。
修复方案1:动画结束后重置元素定位
在每个transition的动画组执行完毕后,添加query操作重置:leave和:enter的position属性,清除残留的fixed定位:
export const slideInAnimation = trigger('routeAnimations', [ transition('watchList => movieDetail', [ style({ position: 'relative' }), query(':enter, :leave', [ style({ position: 'absolute', top: 0, right: 0, width: '100%' }) ]), query(':enter', [style({ right: '-100%', opacity: 0, position: 'fixed' })]), group([ query(':leave', [animate('1s ease-out', style({ right: '100%', opacity: 0 }))]), query(':enter', [animate('1s ease-out', style({ right: '0%', opacity: 1 }))]) ]), // 重置定位状态并隐藏旧元素 query(':leave', [style({ position: 'absolute', display: 'none' })]), query(':enter', [style({ position: 'absolute' })]) ]), transition('movieDetail => watchList', [ style({ position: 'relative' }), query(':enter, :leave', [ style({ position: 'absolute', top: 0, left: 0, width: '100%' }) ]), query(':enter', [style({ left: '-100%', opacity: 0 })]), query(':leave', [style({ position: 'fixed' })]), group([ query(':leave', [animate('1s ease-out', style({ left: '100%', opacity: 0 }))]), query(':enter', [animate('1s ease-out', style({ left: '0%', opacity: 1 }))]) ]), // 重置定位状态并隐藏旧元素 query(':leave', [style({ position: 'absolute', display: 'none' })]), query(':enter', [style({ position: 'absolute' })]) ]) ]);
修复方案2:调整滚动恢复时机
将滚动恢复逻辑延迟到动画完全结束后执行,避免和动画执行过程冲突:
- 在组件中添加动画结束事件监听:
import { AnimationEvent } from '@angular/animations'; onAnimationEnd(event: AnimationEvent) { // 确认动画完成后执行滚动恢复 if (['watchList', 'movieDetail'].includes(event.toState)) { this.mainContent.scrollTo({ top: this.scrollService.scrollTopSubject$.getValue() }); } }
- 在模板中绑定动画结束事件:
<div [@routeAnimations]="getRouteAnimationState()" (@routeAnimations.done)="onAnimationEnd($event)"> <router-outlet></router-outlet> </div>
验证效果
应用上述修改后,重复进行路由切换操作,旧内容闪烁的问题应该会被解决。建议优先组合使用两种方案,既能清除残留的定位状态,又能避免滚动操作干扰动画渲染。
内容的提问来源于stack exchange,提问作者Peter Boomsma
相关产品推荐
相关产品推荐

