无法使用 Angular 动画正确设置轮播
Not able to set Carousel properly using Angular Animations
我使用 Angular 动画创建了以下旋转木马:
我创建了 5 个定义每个状态位置的变量:
但是当我尝试添加更多数据时,有些项目只是从动画中消失(如上图中的 'Joe' 和 'Hidde')。
我正在努力实现以下目标:
(i) 如果数据更多,我需要继续播放动画(不知从何处进入右侧的 DOM,我想我们可以使用 ':enter' 和 ':leave 做到这一点' 别名但不能在这里应用它们。)
(ii) 假设只有两个项目,那么项目应该在 'current' 和 'next' 状态之间进行动画处理,但不会移动到最右边。
这里是 Stackblitz link。
这是代码:
.ts:
animations: [
trigger("carousel", [
state(
"current",
style({
position: "absolute",
top: "16%",
right: "54%",
bottom: "39%",
left: "40%"
})
),
state(
"next",
style({
position: "absolute",
top: "51%",
right: "77%",
bottom: "calc(2% + 3px)",
left: "calc(1% + 6px)"
})
),
state(
"futureNext",
style({
position: "absolute",
top: "51%",
right: "54%",
bottom: "calc(2% + 3px)",
left: "calc(26% + 5px)"
})
),
state(
"postFutureNext",
style({
position: "absolute",
top: "51%",
right: "31%",
bottom: "calc(2% + 3px)",
left: "calc(51% + 5px)"
})
),
state(
"nextToPostFutureNext",
style({
position: "absolute",
top: "51%",
right: "8%",
bottom: "calc(2% + 3px)",
left: "calc(76% + 4px)"
})
),
transition(
"current <=> nextToPostFutureNext",
animate("2000ms ease-in-out")
),
transition(
"nextToPostFutureNext <=> postFutureNext",
animate("2000ms ease-in-out")
),
transition(
"postFutureNext <=> futureNext",
animate("2000ms ease-in-out")
),
transition("futureNext <=> next", animate("2000ms ease-in-out")),
transition("next <=> current", animate("2000ms ease-in-out"))
])
]
clearTimeOutVar: any;
current = 0;
next = 1;
futureNext = 2;
postFutureNext = 3;
nextToPostFutureNext = 4;
ngOnInit() {
this.runSlideShow();
}
runSlideShow() {
this.clearTimeOutVar = setTimeout(() => {
this.updateAnimationVariables();
this.runSlideShow();
}, 3000);
}
updateAnimationVariables() {
if (this.current === 4) {
this.current = 0;
} else if (this.current < 4) {
this.current++;
}
if (this.next === 4) {
this.next = 0;
} else if (this.next < 4) {
this.next++;
}
if (this.futureNext === 4) {
this.futureNext = 0;
} else if (this.futureNext < 4) {
this.futureNext++;
}
if (this.postFutureNext === 4) {
this.postFutureNext = 0;
} else if (this.postFutureNext < 4) {
this.postFutureNext++;
}
if (this.nextToPostFutureNext === 4) {
this.nextToPostFutureNext = 0;
} else if (this.nextToPostFutureNext < 4) {
this.nextToPostFutureNext++;
}
}
ngOnDestroy() {
clearTimeout(this.clearTimeOutVar);
}
.html:
<div class="container">
<div class="header">
A Simple Carousel
</div>
<div class="data">
<div class="cards" *ngFor="let item of data; let i = index;" [@carousel]="
i == current ? 'current' :
i == next ? 'next' :
i == futureNext ? 'futureNext' :
i == postFutureNext ? 'postFutureNext' :
i == nextToPostFutureNext ? 'nextToPostFutureNext' : ''
">
<div class="name">{{ item.name }}</div>
<div class="age">{{ item.age }}</div>
</div>
</div>
</div>
如果我无法解释我的方法中的任何一点,请告诉我。
谢谢。
我很难想象你想做什么样的动画。顺便说一句(我希望不要混淆你),你只能通过代码创建动画。有些人喜欢
想法是在构造函数中注入动画构建器
constructor(private builder: AnimationBuilder) {}
在它总是相同之后,我们使用 ViewChildren 和其他使用 ViewChild 的元素来制作动画
@ViewChildren("card") items: QueryList<ElementRef>;
@ViewChild("container") container: ElementRef;
@ViewChild("dataInit") dataInit: ElementRef;
我们创建了一个函数来计算辅助变量,使我们能够计算项目的最终位置
calculateDimensions() {
this.rectElement = this.items.first.nativeElement.getBoundingClientRect();
const rect = this.dataInit.nativeElement.getBoundingClientRect();
const rectContainer = this.container.nativeElement.getBoundingClientRect();
this.rect = {};
this.rect.height = rectContainer.height - rect.y;
this.rect.y = rect.y;
this.rect.width = rectContainer.width - this.rectElement.width;
}
并且在 ngAfterViewInit 中调用这个函数和一个动画函数
ngAfterViewInit() {
this.calculateDimensions();
this.animateCarousel();
}
让我们使用函数 animateCarousel
animateCarousel() {
//with each element in ViewChildren
this.items.forEach((item: ElementRef, index: number) => {
//I calcule the "order" to change the z-index of the element
this.order[index] =this.items.length-
((index + this.selectedIndex) % this.items.length);
//Calcule the style final
const itemStyle = this.getStyle2(index);
//create an animation using this.builder.build
const myAnimation = this.builder.build([
animate(this.timing, style(itemStyle))
]);
//Use a variable "player" declared: private player: AnimationPlayer;
//and the animation.create over the "nativeElement"
this.player = myAnimation.create(item.nativeElement);
//As we want an infinite carousel, with one element (I choose the first)
if (index == 0)
//when finish,
this.player.onDone(() => {
this.calculateDimensions(); //recalcule the dimensions
//this makes that if resize the navigator
//the animation works well
//add or substract the "selectedIndex" using % operator
//to always gets values from 0 to this.items.length-1
this.selectedIndex =
(this.selectedIndex + this.items.length - 1) % this.items.length;
//and call to the function again
this.animateCarousel();
});
//Finally say player.play()
this.player.play();
});
}
嗯,函数 getStyles 可以是我们想要的,但总有办法
getStyle(index: number) {
//get the "pos" that it's always
const pos = (index + this.selectedIndex) % this.items.length;
//now we return the style we want, e.g.
if (pos == 0)
return { top: this.rect.y + "px", left: this.rect.width / 2 + "px" };
return {
top: this.rect.height - this.rectElement.height / 2 + "px",
left: pos * (this.rect.width / this.items.length) + "px"
};
}
.html
<div #container class="container">
<div class="header">
A Simple Carousel
</div>
<!--this div it's only to situate the carousel-->
<div #dataInit class="data"></div>
<!--see how change the "z-index"
<div #card class="cards" [ngStyle]="{'z-index':order[i]}"
*ngFor="let item of data; let i = index;">
<div class="name">{{ item.name }}</div>
<div class="age">{{ item.age }}</div>
</div>
</div>
可以看到the final result
我使用 Angular 动画创建了以下旋转木马:
我创建了 5 个定义每个状态位置的变量:
但是当我尝试添加更多数据时,有些项目只是从动画中消失(如上图中的 'Joe' 和 'Hidde')。
我正在努力实现以下目标:
(i) 如果数据更多,我需要继续播放动画(不知从何处进入右侧的 DOM,我想我们可以使用 ':enter' 和 ':leave 做到这一点' 别名但不能在这里应用它们。)
(ii) 假设只有两个项目,那么项目应该在 'current' 和 'next' 状态之间进行动画处理,但不会移动到最右边。
这里是 Stackblitz link。
这是代码:
.ts:
animations: [
trigger("carousel", [
state(
"current",
style({
position: "absolute",
top: "16%",
right: "54%",
bottom: "39%",
left: "40%"
})
),
state(
"next",
style({
position: "absolute",
top: "51%",
right: "77%",
bottom: "calc(2% + 3px)",
left: "calc(1% + 6px)"
})
),
state(
"futureNext",
style({
position: "absolute",
top: "51%",
right: "54%",
bottom: "calc(2% + 3px)",
left: "calc(26% + 5px)"
})
),
state(
"postFutureNext",
style({
position: "absolute",
top: "51%",
right: "31%",
bottom: "calc(2% + 3px)",
left: "calc(51% + 5px)"
})
),
state(
"nextToPostFutureNext",
style({
position: "absolute",
top: "51%",
right: "8%",
bottom: "calc(2% + 3px)",
left: "calc(76% + 4px)"
})
),
transition(
"current <=> nextToPostFutureNext",
animate("2000ms ease-in-out")
),
transition(
"nextToPostFutureNext <=> postFutureNext",
animate("2000ms ease-in-out")
),
transition(
"postFutureNext <=> futureNext",
animate("2000ms ease-in-out")
),
transition("futureNext <=> next", animate("2000ms ease-in-out")),
transition("next <=> current", animate("2000ms ease-in-out"))
])
]
clearTimeOutVar: any;
current = 0;
next = 1;
futureNext = 2;
postFutureNext = 3;
nextToPostFutureNext = 4;
ngOnInit() {
this.runSlideShow();
}
runSlideShow() {
this.clearTimeOutVar = setTimeout(() => {
this.updateAnimationVariables();
this.runSlideShow();
}, 3000);
}
updateAnimationVariables() {
if (this.current === 4) {
this.current = 0;
} else if (this.current < 4) {
this.current++;
}
if (this.next === 4) {
this.next = 0;
} else if (this.next < 4) {
this.next++;
}
if (this.futureNext === 4) {
this.futureNext = 0;
} else if (this.futureNext < 4) {
this.futureNext++;
}
if (this.postFutureNext === 4) {
this.postFutureNext = 0;
} else if (this.postFutureNext < 4) {
this.postFutureNext++;
}
if (this.nextToPostFutureNext === 4) {
this.nextToPostFutureNext = 0;
} else if (this.nextToPostFutureNext < 4) {
this.nextToPostFutureNext++;
}
}
ngOnDestroy() {
clearTimeout(this.clearTimeOutVar);
}
.html:
<div class="container">
<div class="header">
A Simple Carousel
</div>
<div class="data">
<div class="cards" *ngFor="let item of data; let i = index;" [@carousel]="
i == current ? 'current' :
i == next ? 'next' :
i == futureNext ? 'futureNext' :
i == postFutureNext ? 'postFutureNext' :
i == nextToPostFutureNext ? 'nextToPostFutureNext' : ''
">
<div class="name">{{ item.name }}</div>
<div class="age">{{ item.age }}</div>
</div>
</div>
</div>
如果我无法解释我的方法中的任何一点,请告诉我。
谢谢。
我很难想象你想做什么样的动画。顺便说一句(我希望不要混淆你),你只能通过代码创建动画。有些人喜欢
想法是在构造函数中注入动画构建器
constructor(private builder: AnimationBuilder) {}
在它总是相同之后,我们使用 ViewChildren 和其他使用 ViewChild 的元素来制作动画
@ViewChildren("card") items: QueryList<ElementRef>;
@ViewChild("container") container: ElementRef;
@ViewChild("dataInit") dataInit: ElementRef;
我们创建了一个函数来计算辅助变量,使我们能够计算项目的最终位置
calculateDimensions() {
this.rectElement = this.items.first.nativeElement.getBoundingClientRect();
const rect = this.dataInit.nativeElement.getBoundingClientRect();
const rectContainer = this.container.nativeElement.getBoundingClientRect();
this.rect = {};
this.rect.height = rectContainer.height - rect.y;
this.rect.y = rect.y;
this.rect.width = rectContainer.width - this.rectElement.width;
}
并且在 ngAfterViewInit 中调用这个函数和一个动画函数
ngAfterViewInit() {
this.calculateDimensions();
this.animateCarousel();
}
让我们使用函数 animateCarousel
animateCarousel() {
//with each element in ViewChildren
this.items.forEach((item: ElementRef, index: number) => {
//I calcule the "order" to change the z-index of the element
this.order[index] =this.items.length-
((index + this.selectedIndex) % this.items.length);
//Calcule the style final
const itemStyle = this.getStyle2(index);
//create an animation using this.builder.build
const myAnimation = this.builder.build([
animate(this.timing, style(itemStyle))
]);
//Use a variable "player" declared: private player: AnimationPlayer;
//and the animation.create over the "nativeElement"
this.player = myAnimation.create(item.nativeElement);
//As we want an infinite carousel, with one element (I choose the first)
if (index == 0)
//when finish,
this.player.onDone(() => {
this.calculateDimensions(); //recalcule the dimensions
//this makes that if resize the navigator
//the animation works well
//add or substract the "selectedIndex" using % operator
//to always gets values from 0 to this.items.length-1
this.selectedIndex =
(this.selectedIndex + this.items.length - 1) % this.items.length;
//and call to the function again
this.animateCarousel();
});
//Finally say player.play()
this.player.play();
});
}
嗯,函数 getStyles 可以是我们想要的,但总有办法
getStyle(index: number) {
//get the "pos" that it's always
const pos = (index + this.selectedIndex) % this.items.length;
//now we return the style we want, e.g.
if (pos == 0)
return { top: this.rect.y + "px", left: this.rect.width / 2 + "px" };
return {
top: this.rect.height - this.rectElement.height / 2 + "px",
left: pos * (this.rect.width / this.items.length) + "px"
};
}
.html
<div #container class="container">
<div class="header">
A Simple Carousel
</div>
<!--this div it's only to situate the carousel-->
<div #dataInit class="data"></div>
<!--see how change the "z-index"
<div #card class="cards" [ngStyle]="{'z-index':order[i]}"
*ngFor="let item of data; let i = index;">
<div class="name">{{ item.name }}</div>
<div class="age">{{ item.age }}</div>
</div>
</div>
可以看到the final result