【问题标题】:Ionic/Angular + Lottie: animate each Lottie individually?Ionic/Angular + Lottie:分别为每个 Lottie 设置动画?
【发布时间】:2020-07-24 13:18:07
【问题描述】:

在我继续之前,我应该注意我之前发布了一个类似的问题,内容与此类似,但是我认为错误的来源是在ngFor 循环。我发现这不是问题,所以我删除了原来的问题,以使我的查询更加准确并且不误导任何人。

我目前正在使用 Ionic 5 和 Angular 9 开发一个项目,我们必须在某些视图中使用 lotties。我正在使用 ngx-lottie 库 (https://github.com/ngx-lottie/ngx-lottie#api),出于我的目的,我没有像库的 API 所示那样在每个视图的模板中插入 lottie,而是构建了一个要包含在其他视图中的组件。该组件的代码如下:

import { Component, OnInit, OnChanges, SimpleChanges, Input } from '@angular/core';
import { AnimationItem } from 'lottie-web';
import { AnimationOptions } from 'ngx-lottie';

@Component({
  selector: 'app-lottie-player',
  template: `<ng-lottie
  [options]="options"
  [styles]="styles"
  (animationCreated)="animationCreated($event)">
  </ng-lottie>`,
})

export class LottiePlayerComponent implements OnInit, OnChanges {

  @Input() lottieToPlay: string;        // Route to the json within assets/lotties
  @Input() lottieToUpdate: string;      // Secondary json in case a switch is needed
  @Input() animationDirection: number;  // 1 for normal direction, -1 for reversed animation
  @Input() dimensions: any;             // Layout specifications passed from parent component

  options: AnimationOptions = {
    path: '',
  };

  styles: Partial<CSSStyleDeclaration> = {
    margin: '0 auto',
  };

  private animationItem: AnimationItem;

  constructor() { }

  ngOnInit() {
    this.updateAnimation();
  }

  ngOnChanges(changes: SimpleChanges) {
    const directionChange = changes.animationDirection;
    if (directionChange && !directionChange.firstChange) {
      this.playAnimation(this.animationItem, directionChange.currentValue);
    }
  }

  updateAnimation(): void {
    this.options = {
      ...this.options,
      path: this.lottieToPlay,
    };
    this.styles = {
      ...this.styles,
      ...this.dimensions
    };
  }

  animationCreated(animationItem: AnimationItem): void {
    this.animationItem = animationItem;
    this.animationItem.autoplay = false;
    this.animationItem.loop = false;
  }

  playAnimation(animationItem: AnimationItem, direction?: number): void {
    this.animationItem = animationItem;
    this.animationItem.setDirection(Math.sign(direction) === 1 ? 1 : -1);
    this.animationItem.play();
  }

}

该组件主要执行以下操作:

  • 从插入的任何父组件接收要呈现的 json。
  • 从所述父级接收自定义 css 属性和抽奖的方向。
  • 在“暂停”状态下启动抽奖,以触发点击动画。

所以当我将它插入到这样的视图中时:

    <ion-buttons slot="secondary">
      <ion-button class="icon-container" (click)="changeIconAndNavigate()">
        <app-lottie-player
          [dimensions]="lottieClassSpecs"
          [lottieToPlay]="lottieFav"
          [animationDirection]="lottieAnimDirection">
        </app-lottie-player>
      </ion-button>
    </ion-buttons>

在具有类似方法的父组件内部:

  public changeIconAndNavigate() {
    this.lottieAnimDirection = 1;
  }

彩票按预期工作。动画只触发一次并停留在其完成的动画状态。

当我像上面那样插入 lottie 时出现问题,但是在同一个组件中进行了多次插入而不是单个插入。我现在相信问题的原因可能是我构建了错误的原始组件,可以这么说。在另一个组件中,我插入了这样的 lottie-player:

<ion-item *ngFor="let card of cards" class="card-info">
...
          <ion-item lines="full">
            <span class="card-span">{{ card.title }}</span>
            <ion-button class="lottie-holder" (click)="toggleSingleSubscription(card)">
              <app-lottie-player
              [dimensions]="lottieClassSpecs"
              [lottieToPlay]="lottieAdd"
              [animationDirection]="lottieAnimDirection">
              </app-lottie-player>
            </ion-button>
          </ion-item>
...
</ion-item>

为了确定,我在 ngFor 循环之外插入了另一个 lottie-player 实例,看看它会如何反应。控制这些cards 的整个父组件只是简单地将它们插入或弹出到一个数组中,并且每次插入后,我根据需要将this.lottieANimDirection 更改为1 或-1,以便lottie 组件中的ngOnChanges可以拾取变化并相应地渲染动画

我期望看到的是每个乐蒂都单独制作动画 - 但他们都这样做了。因此,如果我单击底部的“订阅”...

上面的抽签也有动画。

不仅是上面卡片中的那个,还有用于测试目的的&lt;app-lottie-player&gt; 我尝试在 ngFor 循环上方插入也有动画效果。我相信这个错误的原因可能是,由于我是如何构建这个基本组件的,而不是在控制器中创建一个播放器实例,它正在重新更新同一个实例,导致接收到的信息将显示在 DOM 中的所有插入中,因为它们实际上都来自一个实例。但我不知道如何解决它。

如果有人对为什么会发生这个错误有一点点想法,我会全神贯注。

【问题讨论】:

  • toggleSingleSubscription(card) 的代码是什么样的?您还应该显示您的父代码。根据您的 for 循环,在我看来,您正在向每张卡传递相同的 lottie 输入。您可能想要制作一个卡片组件,每个组件都可以管理自己的彩票输入及其状态。
  • @cjd82187 toggleSingleSubscription() 检查卡片的 id 是否在数组中,如果不在则插入,如果不在则弹出,然后更新 this.lottieAnimDirection。起初,我怀疑您的建议(ngLoop 基本上向每个人提供相同的抽签输入)是原因。但是,在 ngFor 之外的 lottie 也进行动画处理是没有意义的
  • 是的,因为循环没有自己的范围。您的父组件中只有一个 this. lottieANimDirection,并将该值传递给其模板中的所有内容
  • 我添加了一个带有堆栈闪电战的答案,说明了我正在谈论的问题

标签: angular ionic-framework lottie


【解决方案1】:

我整理了一个堆栈闪电战,展示了我认为您的问题所在,即所有组件都传递了相同的状态实例。

https://stackblitz.com/edit/angular-ivy-lfckkn

divs 的第一个数组在父级中都有一个状态,因此将其切换到一个状态会更新所有状态。

app-cards 的第二个都是他们自己的组件,有自己的状态。请注意,由于我们仍然将全局状态作为输入传递,而在内部更改它时,它不会更新其他状态,但如果它在父级更新,它将将该新值向下推送给所有子级。

子组件示例

import { Component, OnInit, Input} from '@angular/core';

@Component({
  selector: 'app-card',
  templateUrl: './card.component.html',
  styleUrls: ['./card.component.css']
})
export class CardComponent implements OnInit {
  @Input() card: {id: number, title: string};
  internalState = true;
  @Input() stateFromParent: boolean;
  constructor() { }

  ngOnInit() {
  }

  toggleInternalState() {
    this.internalState  = !this.internalState;
    this.stateFromParent = !this.stateFromParent;
  }

}

子组件模板


<div style="border: 1px solid green; padding: 5px;">
<p>ID: {{card.id}}</p>
<p>Title: {{card.title}}</p>
<p>internalState: {{internalState | json}}</p>
<p>stateFromParent: {{stateFromParent | json}} (NOTE: it will mange its own internal one, but if the parent's input changes that new value will be passed in)</p>
<button (click)="toggleInternalState()">Toggle Value</button>
</div>

【讨论】:

  • 这样的事情有时让我质疑我作为 Angular 开发人员的能力...如果这不是我第一次使用 ngFor 或 ngIf 等条件“按需”创建卡片,那么这种疏忽会一直正常。无论如何,你成功了——我没有使用 ngFor 在父模板中生成卡片,而是为它们构建了一个子组件,因此每个组件都有自己的状态和app-lottie-player 插入和瞧。你现在可以独立动画了
猜你喜欢
  • 2021-07-13
  • 1970-01-01
  • 2019-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-24
  • 2022-10-23
  • 2023-02-17
相关资源
最近更新 更多