You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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),
        () => {});
}
问题分析
  1. 统一绑定无法单独控制:所有图片绑定同一个src$ Observable,没法实现每张图片的独立状态切换。
  2. DOM操作违背Angular机制:直接通过document.getElementsByClassName修改DOM属性,脱离了数据绑定逻辑,容易出现状态不一致。
  3. 定时器滥用:在RxJS订阅里嵌套setInterval,会给每张图片创建重复触发的定时器,导致图片一直被设为img_2,没有重置逻辑。
  4. 状态切换逻辑缺失:原代码没处理“将之前的图片恢复为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:18:24