在Angular中工作5.尝试从头开始创建轮播类型的图像浏览器。用户可以单击/滑动以查看下一个或上一个图像。
:enter
类型的动画运行良好(在我的代码中显示为"void => next"
和"void => prev"
)。但是,"next => void"
和"prev => void"
的动画过渡并未发生。
网络上的每个答案似乎都围绕着子组件,为元素display: block
制作样式并在更改状态后调用detectChanges()
。这不是子组件方案,我已经为元素分配了"display: block"
css甚至可以将其包括在动画样式中,并且我确保正确添加了detectChanges()
状态改变后。这些都不起作用。
我在某处读到一条评论,该内容发现detectChanges()不再具有:leave动画的技巧。他们的解决方法是在setTimeout()回调中包装将元素从DOM中删除的代码。 即使这不起作用。
我只从https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts复制/粘贴整个代码块而已,只更改了变量名。但是,即使那样也不起作用!
这使我掉头发。请帮助我。
组件(打字稿中为Angular 5)
import { Component, OnInit, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
import { trigger, transition, style, animate, keyframes } from '@angular/animations';
type Orientation = ('prev' | 'next' | 'none');
@Component({
selector: 'app-album-browser',
templateUrl: './album-browser.component.html',
styleUrls: ['./album-browser.component.scss'],
animations: [
trigger('animateCarousel',
[
transition('prev => void', // ------> leaving ------->
[
animate('500ms ease-in-out', keyframes([
style({ opacity: 1.0, left: 0 }),
style({ opacity: 0.0, left: 200 })
]))
]),
transition('void => prev', // ----> entering ---->
[
animate('500ms ease-in-out', keyframes([
style({ opacity: 0.0, left: -200, zIndex: 2 }),
style({ opacity: 1.0, left: 0, zIndex: 2 })
]))
]),
transition('next => void', // <------- leaving <-------
[
animate('500ms ease-in-out', keyframes([
style({ opacity: 1.0, right: 0 }),
style({ opacity: 0.0, right: 200 })
]))
]),
transition('void => next', // <------- entering <--------
[
animate('500ms ease-in-out', keyframes([
style({ opacity: 0.0, right: -200, zIndex: 2 }),
style({ opacity: 1.0, right: 0, zIndex: 2 })
]))
])
])
]
})
export class AlbumBrowserComponent implements OnInit, AfterViewInit {
private readonly SWIPE_ACTION = { LEFT: 'swipeleft', RIGHT: 'swiperight'};
public readonly LEFT = 'LEFT';
public readonly RIGHT = 'RIGHT';
public orientation: Orientation = 'none';
public images: string[] = [];
public selectedImage: string[] = [];
public changeDetectorRef: ChangeDetectorRef;
constructor(changeDetectorRef: ChangeDetectorRef) {
this.changeDetectorRef = changeDetectorRef;
this.images.push('../../../assets/images/1.jpg');
this.images.push('../../../assets/images/2.jpg');
this.images.push('../../../assets/images/3.jpg');
this.selectedImage.push(this.images[0]);
}
ngOnInit() {
}
public click(direction: string) {
if (direction === this.LEFT) {
this.swipe(this.SWIPE_ACTION.LEFT);
}
if (direction === this.RIGHT) {
this.swipe(this.SWIPE_ACTION.RIGHT);
}
}
public swipe(action = this.SWIPE_ACTION.RIGHT) {
let res: string;
const index = this.images.indexOf(this.selectedImage[0]);
if (action === this.SWIPE_ACTION.LEFT) {
this.orientation = 'next';
this.selectedImage = [];
this.changeDetectorRef.detectChanges();
res = !!this.images[index + 1] ?
this.images[index + 1] :
this.images[0];
}
if (action === this.SWIPE_ACTION.RIGHT) {
this.orientation = 'prev';
this.selectedImage = [];
this.changeDetectorRef.detectChanges();
res = !!this.images[index - 1] ?
this.images[index - 1] :
this.images[this.images.length - 1];
}
this.selectedImage.push(res);
}
}
模板
<div class="album-browser-container">
<div class="left arrow small-glow" (click)="click(LEFT)"></div>
<div class="viewport-frame glow">
<div class="viewport">
<div class="image-slider"
(swipeleft)="swipe($event.type)"
(swiperight)="swipe($event.type)">
<div class="carousel"
*ngFor="let image of selectedImage">
<div class="image-container"
[@animateCarousel]="orientation">
<img [src]="image" class="album-image">
</div>
</div>
</div>
</div>
</div>
<div class="right arrow small-glow" (click)="click(RIGHT)"></div>
</div>
答案 0 :(得分:0)
TLDR;使用* ngIf而不是* ngFor并将更新后的selectedImage包装在setTimeout回调中。
我在调试解决方案时遇到了解决方案。在调试时,当在数组重置行上放置断点时,我看到了离开动画起作用。我在上面发布的GitHub条目(https://github.com/born2net/Angular-kitchen-sink/blob/master/src/comps/app2/notes/AnimateCards.ts)吸引了我大部分时间,似乎更高版本的Angular产生了以稍微不同的方式处理动画的需求。
我使用的是绑定到组件上名为“ showIt”的类成员的*ngFor
,而不是在“图像容器” div中使用ngIf
指令。我更新了方向并首先检测detectChanges()。然后将“ showIt”更新为FALSE,然后再次调用detectChanges()。这看起来似乎是多余的,但是当我省略对detectChanges()的增量调用时,几乎似乎在“队列”(由于缺少更好的术语)中留下了DOM更改,这些更改最终在后续对detectChanges()的调用中执行)。然后,我将images数组的目标索引存储在局部变量中,最后包装后续调用以在setTimeout回调中更新selectedImage,并将超时设置为与过渡动画相同的时间。
如果您在考虑“必须在setTimeout回调中包装代码有多么不雅”,我完全同意。但这似乎只是确保:enter
和:leave
动画都能够蒸发的唯一方法。我最初问题的根本原因是,当selectedImage数组设置为空时,DOM的更新速度如此之快,以至于动画甚至在人眼无法察觉之前都被覆盖。
最终模板
<div class="album-browser-container">
<div class="left arrow small-glow" (click)="click(LEFT)"></div>
<div class="viewport-frame glow">
<div class="viewport">
<div class="image-slider"
(swipeleft)="swipe($event.type)"
(swiperight)="swipe($event.type)">
<div class="carousel">
<div class="image-container"
*ngIf="showIt"
[@animateCarousel]="orientation">
<img [src]="selectedImage[0]"
class="album-image"
(swipeleft)="swipe($event.type)"
(swiperight)="swipe($event.type)">
</div>
</div>
</div>
</div>
</div>
<div class="right arrow small-glow" (click)="click(RIGHT)"></div>
</div>
我的OP中的动画实际上是准确的。仅单击/滑动方法已更改。
组件(被截断)
public swipe(action = this.SWIPE_ACTION.RIGHT) {
let res: string;
if (action === this.SWIPE_ACTION.LEFT) {
this.orientation = 'next';
this.changeDetectorRef.detectChanges();
this.showIt = false;
this.changeDetectorRef.detectChanges();
const index = this.images.indexOf(this.selectedImage[0]);
res = !!this.images[index + 1] ?
this.images[index + 1] :
this.images[0];
setTimeout(() => {
this.selectedImage = [];
this.selectedImage.push(res);
this.showIt = true;
this.changeDetectorRef.detectChanges();
}, 300);
}
if (action === this.SWIPE_ACTION.RIGHT) {
this.orientation = 'prev';
this.changeDetectorRef.detectChanges();
this.showIt = false;
this.changeDetectorRef.detectChanges();
const index = this.images.indexOf(this.selectedImage[0]);
res = !!this.images[index - 1] ?
this.images[index - 1] :
this.images[this.images.length - 1];
setTimeout(() => {
this.selectedImage = [];
this.selectedImage.push(res);
this.showIt = true;
this.changeDetectorRef.detectChanges();
}, 300);
}
}
答案 1 :(得分:0)
你可以像这样使用 :leave 代替 'state => void',它会在元素从 DOM 中移除时触发这个转换(如果你使用结构指令喜欢 *ngIf)
transition(':leave', //your style goes here)