Ionic/Angular + Lottie:分别为每个 Lottie 制作动画?
Ionic/Angular + Lottie: animate each Lottie individually?
在我继续之前,我应该注意到我之前发布了一个类似的问题,内容与这个类似,但是我认为错误的来源是使用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 属性和乐透游戏的方向。
- 以 'paused' 状态启动 lottie,以在点击时触发动画。
所以当我将它插入到这样的视图中时:
<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 时出现问题,但在同一个组件中插入了多个而不是一个。我现在相信问题的原因可能是我构建了错误的原始组件,可以这么说。在另一个组件中,我像这样插入了 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,以便 ngOnChanges在 lottie 组件中可以获取更改并相应地渲染动画
我希望看到的是每个 lottie 都单独制作动画 - 但他们都这样做了。因此,如果我单击底部的那个,上面写着 'subscribe'...
上面的 lottie 也有动画效果。
不仅是上面卡片中的那个,还有用于测试目的的 <app-lottie-player>
我尝试在 ngFor 循环上方插入动画。我相信错误的原因可能是因为我构建这个基本组件的方式,而不是在控制器中创建播放器的一个实例,它正在重新更新同一个实例,导致接收到的信息显示在 DOM 中的所有插入中,因为它们实际上都来自一个实例。但是我不知道怎么解决。
如果有人对为什么彩票中会出现这个错误有丝毫的想法,我就是 ears/eyes。
我整理了一个 stackblitz,展示了我认为你的问题所在,即所有组件都传递了相同的状态实例。
https://stackblitz.com/edit/angular-ivy-lfckkn
div
的第一个数组在父级中都只有一个状态,因此在一个中切换它会更新所有状态。
app-card
中的第二个都是它们自己的组件,有自己的状态。请注意,由于我们仍在将全局状态作为输入传递,因此在内部更改它时不会更新其他状态,但如果它在父级更新,它将把新值推送给所有子级。
子组件示例
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>
在我继续之前,我应该注意到我之前发布了一个类似的问题,内容与这个类似,但是我认为错误的来源是使用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 属性和乐透游戏的方向。
- 以 'paused' 状态启动 lottie,以在点击时触发动画。
所以当我将它插入到这样的视图中时:
<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 时出现问题,但在同一个组件中插入了多个而不是一个。我现在相信问题的原因可能是我构建了错误的原始组件,可以这么说。在另一个组件中,我像这样插入了 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,以便 ngOnChanges在 lottie 组件中可以获取更改并相应地渲染动画
我希望看到的是每个 lottie 都单独制作动画 - 但他们都这样做了。因此,如果我单击底部的那个,上面写着 'subscribe'...
上面的 lottie 也有动画效果。
不仅是上面卡片中的那个,还有用于测试目的的 <app-lottie-player>
我尝试在 ngFor 循环上方插入动画。我相信错误的原因可能是因为我构建这个基本组件的方式,而不是在控制器中创建播放器的一个实例,它正在重新更新同一个实例,导致接收到的信息显示在 DOM 中的所有插入中,因为它们实际上都来自一个实例。但是我不知道怎么解决。
如果有人对为什么彩票中会出现这个错误有丝毫的想法,我就是 ears/eyes。
我整理了一个 stackblitz,展示了我认为你的问题所在,即所有组件都传递了相同的状态实例。
https://stackblitz.com/edit/angular-ivy-lfckkn
div
的第一个数组在父级中都只有一个状态,因此在一个中切换它会更新所有状态。
app-card
中的第二个都是它们自己的组件,有自己的状态。请注意,由于我们仍在将全局状态作为输入传递,因此在内部更改它时不会更新其他状态,但如果它在父级更新,它将把新值推送给所有子级。
子组件示例
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>