Angular按自定义序列切换图片src的动画实现问题
问题描述
我有7张用[attr.src]绑定的图片,需要按特定序列切换src实现动画:初始仅第一张显示img_2,之后依次是第一张+第二张、第二张+第三张……直至最后,动画结束后循环。要求始终只有相邻的两张(除初始状态)显示img_2,其余显示img_1。用RxJS实现时动画仅部分生效,之前的图片无法恢复为img_1的src,求解决方法。
原代码
HTML模板
<div> <img class="first-arrow" [attr.src]="src$ | async"/> <img class="second-arrow" [attr.src]="src$ | async"/> <img class="third-arrow" [attr.src]="src$ | async"/> <img class="fourth-arrow" [attr.src]="src$ | async"/> <img class="fifth-arrow" [attr.src]="src$ | async"/> <img class="sixth-arrow" [attr.src]="src$ | async"/> <img class="seventh-arrow" [attr.src]="src$ | async"/> </div>
TypeScript组件
src$: Observable<string> | undefined; isRedVisible = true; red = '1'; yellow = '2'; arrow1 = document.getElementsByClassName('first-arrow'); arrow2 = document.getElementsByClassName('second-arrow'); arrow3 = document.getElementsByClassName('third-arrow'); arrow4 = document.getElementsByClassName('fourth-arrow'); arrow5 = document.getElementsByClassName('fifth-arrow'); arrow6 = document.getElementsByClassName('sixth-arrow'); arrow7 = document.getElementsByClassName('seventh-arrow'); arrowsArray: Array<any> = []; ngOnInit(): void { this.arrowsArray.push( this.arrow1, this.arrow2, this.arrow3, this.arrow4, this.arrow5, this.arrow6, this.arrow7 ); } ngAfterViewInit() { this.src$ = of('../../assets/img_1.png'); from(this.arrowsArray.reverse()) .pipe(concatMap(v => of(v) .pipe(delay(500))), repeat()).subscribe( (item) => { setInterval(() => { item[0].src = `../../assets/img_${this.yellow}.png` }, 500); }, error => console.log(error), () => {}); }
问题分析
- 统一绑定无法单独控制:所有图片绑定同一个
src$Observable,没法实现每张图片的独立状态切换。 - DOM操作违背Angular机制:直接通过
document.getElementsByClassName修改DOM属性,脱离了数据绑定逻辑,容易出现状态不一致。 - 定时器滥用:在RxJS订阅里嵌套
setInterval,会给每张图片创建重复触发的定时器,导致图片一直被设为img_2,没有重置逻辑。 - 状态切换逻辑缺失:原代码没处理“将之前的图片恢复为
img_1”的步骤,满足不了“仅相邻两张显示img_2”的要求。
解决方法
通过RxJS生成状态序列,结合Angular数据绑定,单独控制每张图片的显示状态:
修改后的代码
HTML模板(用循环简化)
<div> <img *ngFor="let isActive of arrowStates$ | async; index as i" [src]="isActive ? '../../assets/img_2.png' : '../../assets/img_1.png'" [class]="`arrow-${i+1}`"/> </div>
TypeScript组件
import { Component, OnInit } from '@angular/core'; import { Observable, of, concat, repeat, delay } from 'rxjs'; @Component({ selector: 'app-arrow-animation', templateUrl: './arrow-animation.component.html' }) export class ArrowAnimationComponent implements OnInit { arrowStates$!: Observable<boolean[]>; private readonly arrowCount = 7; private readonly animationDelay = 500; // 每一步的停留时间 ngOnInit(): void { // 1. 初始状态:仅第一张显示img_2 const initialState = Array(this.arrowCount).fill(false); initialState[0] = true; const initialState$ = of(initialState).pipe(delay(this.animationDelay)); // 2. 中间状态序列:相邻两张显示img_2 const middleStates$: Observable<boolean[]>[] = []; for (let i = 1; i < this.arrowCount; i++) { const state = Array(this.arrowCount).fill(false); state[i - 1] = true; state[i] = true; middleStates$.push(of(state).pipe(delay(this.animationDelay))); } // 3. 结束状态:仅最后一张显示img_2 const finalState = Array(this.arrowCount).fill(false); finalState[this.arrowCount - 1] = true; const finalState$ = of(finalState).pipe(delay(this.animationDelay)); // 4. 拼接所有状态流并循环 this.arrowStates$ = concat( initialState$, ...middleStates$, finalState$ ).pipe(repeat()); } }
代码说明
- 状态驱动更新:用
boolean[]数组标记每张图片是否显示img_2,通过Observable推送状态变化,模板自动同步更新src。 - RxJS序列控制:用
concat按顺序拼接初始、中间、结束状态的流,每个状态停留指定时间后切换,最后用repeat()实现循环。 - 避免直接DOM操作:完全通过Angular数据绑定更新图片src,符合框架设计原则,状态更可控。
内容的提问来源于stack exchange,提问作者W12
相关产品推荐
相关产品推荐

