如何拦截 Angular 中的 :enter 和 :leave 动画并更改 Diff?
How to intercept :enter & :leave Animations in Angular with a changes Diff?
我的模板中有一个列表,其中将显示前 3 个项目,其余项目位于可折叠的位置,即在 'show more' link 内。
- item1
- item2
item3
显示更多
单击“显示更多”后的外观如下:
- item1
- item2
- item3
- item4
item5
少显示
该列表是高度动态的。 这些项目由名为 myList 的单个列表中的组件保存。可折叠来自我们的模式库,其中预定义了 html 结构。所以我实际上必须在我的模板中有 2 个不同的列表。第一个用于前 3 个项目,第二个用于可折叠的其余项目。为了不使用相同的 HTML 两次,我使用 ng-template 定义列表并重复使用它 2 次。这是模板的粗略结构:
<ng-template #listRef let-list>
<ul>
<ng-container *ngFor="let item of list; trackBy: trackByFn;">
<li @animation>
...
这是模板的用法:
<!-- first part, max 3 items -->
<ng-container *ngTemplateOutlet="listRef; context: {$implicit: myList?.slice(0,3)}"></ng-container>
<!-- second part, rest in collapsible -->
<ng-container *ngIf="lebenslaufEintraege?.length > 3">
<div class="my-collapsible">
<a>...</a>
<div>..
<ng-container
*ngTemplateOutlet="listRef; context: {$implicit: myList?.slice(3,myList.length)}">
</ng-container>
此外,一旦添加了新项目,它就会进入动画列表。此外,如果一个项目被删除,它会保持动画状态。这是我的动画触发器:
trigger('animation', [
transition(':enter', [
style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0'}), // initial
animate('0.5s',
style({ height: '*', 'padding-top': '*', 'padding-bottom': '*'})) // final
]),
transition(':leave', [
style({ height: '*', 'padding-top': '*', 'padding-bottom': '*', opacity: 1}), // initial
animate('0.5s',
style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0', opacity: 0})) // final
])
])
问题是,当我们有超过 3 个项目并且可折叠项打开时,所有项目都在视口中看到。在这种情况下,当一个新项目被添加到列表的第一部分时,它进入动画,但由于列表的最后一个项目也离开第一个列表并进入第二个列表,它也是动画的,我们不这样做想要。
换句话说;如果我们有 5 个项目,并在顶部添加一个新项目,item3 将从第一个列表中删除,并添加到第二个列表中,这也会触发动画.
我怎样才能阻止这个动画?
今天我发现,我们可以使用此注释禁用单个项目的动画:
<li [@.disabled]="item.isAnimated" @animation>
但我无法想象我应该如何实现这个标志 isAnimated 的逻辑。 item3 移到 2.list 后,我必须检查它是否在之前的第一个列表中,但我没有第一个列表的先前状态。
我认为唯一可能的选择是以某种方式拦截动画,并查看列表的先前状态是否具有相同的项目,如果是则中断动画..
或者我应该从项目中删除动画,而是为我的主列表 (myList) 中的更改定义一些动画。但我不知道如何使用我的模板实现这一点..
感谢任何帮助。
与您的方法略有不同。不确定它是否符合您问题的答案(拦截 enter/leave 动画事件),但我认为至少可以解决您的用例。
首先不维护 2 个数组怎么样。
<div *ngFor="let item of list; let i=index; trackBy: trackByFn;">
<li @animation *ngIf="i < defaultCount">
{{item.value}}
</li>
</div>
<button (click)="showMore()" *ngIf="defaultCount === 3">Show More</button>
<button (click)="showLess()" *ngIf="defaultCount !== 3">Show Less</button>
并在您的组件打字稿文件中。
showMore(): void {
this.defaultCount = this.list.count;
}
showLess(): void {
this.defaultCount = 3;
}
您的动画保持原样。现在您的功能应该与动画一起工作。由于没有项目从 1 个数组进入另一个数组的概念,您应该摆脱动画问题和它涉及的其他复杂性。
注:
与显示 More/Less 按钮相关的代码只是为了表达这个想法而进行了简化。请相应地更改代码。
希望对您有所帮助。
您遇到的问题是,您无法区分某个项目是被添加到您的列表中还是只是被移动到位,因为该项目在移动或添加之前不存在于模板中。如果该项目在移动到位之前就存在,那么您可以区分事件并相应地使用 [@.disabled]
。
一个简单但低效的解决方案是重复列表两次并将指示器传递给模板,通知它隐藏前三个元素或隐藏除最后三个元素之外的所有元素。同样的逻辑应该用于禁用动画。
如果我们可以假设一次只能将一个元素添加到数组中,那么一种更有效的方法是只将一个额外的元素传递给模板。对于列表的顶部,传递前四个元素,对于底部,传递从索引 2 开始的所有项目。
简化的工作示例代码
<ng-container *ngTemplateOutlet="listRef; context: {$implicit: items | slice:0:3, hideIndex: 3}"></ng-container>
<ng-container *ngTemplateOutlet="listRef; context: {$implicit: items | slice:2, hideIndex: 0}"></ng-container>
<ng-template #listRef let-list let-hideIndex="hideIndex">
<ul>
<ng-container *ngFor="let x of list; let index = index">
<li @animation
[hidden]="hideIndex == index"
[@.disabled]="hideIndex == index">{{x}}
</li>
</ng-container>
</ul>
</ng-template>
发生的事情是,索引 2 和 3 处的项目将同时出现在两个列表中,在适当的情况下在其中一个列表中隐藏并禁用动画。如果发生转变,相应的项目现在将只出现在列表中之前隐藏它们的位置。动画不会出现,因为它们已经添加到 DOM 之前。
我最初只尝试使用隐藏,但如果快速添加项目,动画的尾端就会显示。这就是 [@.disabled]
也被使用的原因。
我的模板中有一个列表,其中将显示前 3 个项目,其余项目位于可折叠的位置,即在 'show more' link 内。
- item1
- item2
item3
显示更多
单击“显示更多”后的外观如下:
- item1
- item2
- item3
- item4
item5
少显示
该列表是高度动态的。 这些项目由名为 myList 的单个列表中的组件保存。可折叠来自我们的模式库,其中预定义了 html 结构。所以我实际上必须在我的模板中有 2 个不同的列表。第一个用于前 3 个项目,第二个用于可折叠的其余项目。为了不使用相同的 HTML 两次,我使用 ng-template 定义列表并重复使用它 2 次。这是模板的粗略结构:
<ng-template #listRef let-list>
<ul>
<ng-container *ngFor="let item of list; trackBy: trackByFn;">
<li @animation>
...
这是模板的用法:
<!-- first part, max 3 items -->
<ng-container *ngTemplateOutlet="listRef; context: {$implicit: myList?.slice(0,3)}"></ng-container>
<!-- second part, rest in collapsible -->
<ng-container *ngIf="lebenslaufEintraege?.length > 3">
<div class="my-collapsible">
<a>...</a>
<div>..
<ng-container
*ngTemplateOutlet="listRef; context: {$implicit: myList?.slice(3,myList.length)}">
</ng-container>
此外,一旦添加了新项目,它就会进入动画列表。此外,如果一个项目被删除,它会保持动画状态。这是我的动画触发器:
trigger('animation', [
transition(':enter', [
style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0'}), // initial
animate('0.5s',
style({ height: '*', 'padding-top': '*', 'padding-bottom': '*'})) // final
]),
transition(':leave', [
style({ height: '*', 'padding-top': '*', 'padding-bottom': '*', opacity: 1}), // initial
animate('0.5s',
style({ height: '0px', 'padding-top': '0', 'padding-bottom': '0', opacity: 0})) // final
])
])
问题是,当我们有超过 3 个项目并且可折叠项打开时,所有项目都在视口中看到。在这种情况下,当一个新项目被添加到列表的第一部分时,它进入动画,但由于列表的最后一个项目也离开第一个列表并进入第二个列表,它也是动画的,我们不这样做想要。
换句话说;如果我们有 5 个项目,并在顶部添加一个新项目,item3 将从第一个列表中删除,并添加到第二个列表中,这也会触发动画.
我怎样才能阻止这个动画?
今天我发现,我们可以使用此注释禁用单个项目的动画:
<li [@.disabled]="item.isAnimated" @animation>
但我无法想象我应该如何实现这个标志 isAnimated 的逻辑。 item3 移到 2.list 后,我必须检查它是否在之前的第一个列表中,但我没有第一个列表的先前状态。
我认为唯一可能的选择是以某种方式拦截动画,并查看列表的先前状态是否具有相同的项目,如果是则中断动画..
或者我应该从项目中删除动画,而是为我的主列表 (myList) 中的更改定义一些动画。但我不知道如何使用我的模板实现这一点..
感谢任何帮助。
与您的方法略有不同。不确定它是否符合您问题的答案(拦截 enter/leave 动画事件),但我认为至少可以解决您的用例。
首先不维护 2 个数组怎么样。
<div *ngFor="let item of list; let i=index; trackBy: trackByFn;">
<li @animation *ngIf="i < defaultCount">
{{item.value}}
</li>
</div>
<button (click)="showMore()" *ngIf="defaultCount === 3">Show More</button>
<button (click)="showLess()" *ngIf="defaultCount !== 3">Show Less</button>
并在您的组件打字稿文件中。
showMore(): void {
this.defaultCount = this.list.count;
}
showLess(): void {
this.defaultCount = 3;
}
您的动画保持原样。现在您的功能应该与动画一起工作。由于没有项目从 1 个数组进入另一个数组的概念,您应该摆脱动画问题和它涉及的其他复杂性。
注:
与显示 More/Less 按钮相关的代码只是为了表达这个想法而进行了简化。请相应地更改代码。
希望对您有所帮助。
您遇到的问题是,您无法区分某个项目是被添加到您的列表中还是只是被移动到位,因为该项目在移动或添加之前不存在于模板中。如果该项目在移动到位之前就存在,那么您可以区分事件并相应地使用 [@.disabled]
。
一个简单但低效的解决方案是重复列表两次并将指示器传递给模板,通知它隐藏前三个元素或隐藏除最后三个元素之外的所有元素。同样的逻辑应该用于禁用动画。
如果我们可以假设一次只能将一个元素添加到数组中,那么一种更有效的方法是只将一个额外的元素传递给模板。对于列表的顶部,传递前四个元素,对于底部,传递从索引 2 开始的所有项目。
简化的工作示例代码
<ng-container *ngTemplateOutlet="listRef; context: {$implicit: items | slice:0:3, hideIndex: 3}"></ng-container>
<ng-container *ngTemplateOutlet="listRef; context: {$implicit: items | slice:2, hideIndex: 0}"></ng-container>
<ng-template #listRef let-list let-hideIndex="hideIndex">
<ul>
<ng-container *ngFor="let x of list; let index = index">
<li @animation
[hidden]="hideIndex == index"
[@.disabled]="hideIndex == index">{{x}}
</li>
</ng-container>
</ul>
</ng-template>
发生的事情是,索引 2 和 3 处的项目将同时出现在两个列表中,在适当的情况下在其中一个列表中隐藏并禁用动画。如果发生转变,相应的项目现在将只出现在列表中之前隐藏它们的位置。动画不会出现,因为它们已经添加到 DOM 之前。
我最初只尝试使用隐藏,但如果快速添加项目,动画的尾端就会显示。这就是 [@.disabled]
也被使用的原因。