Issue
I'm trying to create a psuedo-3d carousel with 5 items like the following (and have them cycle around):
I found this great stackblitz as a starting point, and have been playing with it for hours trying to make it have 5 items instead of 3 but I'm having a really hard time understanding how it works.
If someone smarter than me can help me understand how to make this 5 items it would be great. In particular, I dont understand how this movements
variable should be configured, and that is probably the entire secret sauce here.
movements = [
{ pos: 0, right: [1, 2], left: [8, 7] },
{ pos: 2, right: [3, 4, 5, 6, 7], left: [1, 0] },
{ pos: 7, right: [8, 0], left: [6, 5, 4, 3, 2] }
];
Solution
sorry, I think I the author. and I explained very bad the code. See that you see in your carousel 5 items at time, So draw a circunference and divide in 8 part. numbered so the most bottom as 0, and to the left 1, 2,... some like
4
3 5
2 6
1 7
0
The animates (the faces you see) are 0,1,2,6 and 7
animates = [0, 1,2,6,7];
And the position are
movements = [
{ pos: 0, right: [1], left: [7] },
{ pos: 1, right: [2], left: [0] },
{ pos: 2, right: [3,4,5,6], left: [1] },
{ pos: 6, right: [7], left: [5,4,3,2] },
{ pos: 7, right: [0], left: [6] },
];
that, e.g. the face is in position 0, to the rigth goes to position 1, to the left goes to position 7 the face is in position 1, to the rigth goes to position 1, to the left goes to position 0 the face is in position 2, to the rigth goes to positions 3,4,5 and 6, to the left goes to position 1.
Well, you need take carefull, when we calculate the angle, you divided by 8 (not by 9)
const animations = mov[direction].map(m => {
const angle = (m * 2 * Math.PI) / 8; //<--see the "8"
....
}
You can choose, instead of divided by 8, divided by 16, to make the movement more realistic. In this case you see the faces
8
7 9
6 10
5 11
4 12
3 13
2 14
1 15
0
See that in this case the faces visible are 0,2,4,12 y 14
animates=[0,2,4,12,14]
The positions are
movements = [
{ pos: 0, right: [1,2], left: [15,14] },
{ pos: 2, right: [3,4], left: [1,0] },
{ pos: 4, right: [5,6,7,8,9,10,11,12], left: [3,2] },
{ pos: 12, right: [13,14], left: [11,10,9,8,7,6,5,4] },
{ pos: 14, right: [15,0], left: [13,12] },
];
And you need divided by 16
const animations = mov[direction].map(m => {
const angle = (m * 2 * Math.PI) / 16; //<--see the "16"
....
}
I forked the stackblitz here
Update the carousel only move to left, rigth one step. We can improve it if we defined other movements to allow move to left and right two steps
movementsTwo=[
{ pos: 0, right: [1,2,3,4], left: [15,14,13,12] },
{ pos: 2, right: [3,4,5,6,7,8,9,10,11,12], left: [1,0,15,14] },
{ pos: 4, right: [5,6,7,8,9,10,11,12,13,14], left: [3,2,1,0] },
{ pos: 12, right: [13,14,15,0], left: [11,10,9,8,7,6,5,4,3,2] },
{ pos: 14, right: [15,0,1,2], left: [13,12,11,10,9,8,7,6,5,4] },
]
And change the function animateViews to allow move one or two steps
animateViews(direction: string,steps:number=1) {
this.animates.forEach((x: number, index: number) => {
//here use one or another array
const movements=steps==1?this.movements:this.movementsTwo;
const mov = movements.find(m => m.pos == x);
...
}
This allow us write a function to "move to front" one element
indexToFront(index: any) {
index = +index;
const pos = this.animates[+index];
if (pos) {
const mov = this.movements.find((x) => x.pos == pos);
const mov2 = this.movementsTwo.find((x) => x.pos == pos);
const anim = { direction: 'right', steps: 1 };
if (
mov.left[mov.left.length - 1] == 0 ||
mov2.left[mov2.left.length - 1] == 0
)
anim.direction = 'left';
if (
mov2.left[mov2.left.length - 1] == 0 ||
mov2.right[mov2.right.length - 1] == 0
)
anim.steps = 2;
this.animateViews(anim.direction, anim.steps);
}
}
Well, the only is write a select
<select #select (change)="indexToFront(select.value)">
<option *ngFor="let i of [0,1,2,3,4]">{{i}}</option>
</select>
Or add a (click)
event to our "carousel"
<div style="position:relative;margin-top:150px">
<div class="carousel" >
<div #element *ngFor="let i of [0,2,4,12,14];let index=index"
(click)="animates[index]!=0 && indexToFront(index)"
class="carousel__cell">{{index}}
</div>
</div>
I create another stackblitz
NOTE: Really "harcoded" the movemnet don't like so much. If I have a bit time I'll try to create a component, but I can not promise when :(
Update 2 I made a component speudo-carousel for 5 images. The main change is that, in this one, I use transfom to move the "cards". So the function animateView becomes like
animateViews(direction: string, steps: number = 1) {
this.animates.forEach((x: number, index: number) => {
const movements = steps == 1 ? this.movements : this.movementsTwo;
const mov = movements.find((m) => m.pos == x);
const item = this.itemsView.find((_x, i) => i == index);
const animations = mov[direction].map((m) => {
const angle = (m * 2 * Math.PI) / 16;
const scale =
(1 + this.minScale) / 2 + ((1 - this.minScale) / 2) * Math.cos(angle);
const applystyle = {
transform:'translate('+
(this.radius * Math.sin(angle)) + 'px,'+
(Math.floor(this.top * scale)-this.top)+'px)
scale('+scale+')',
'z-index': Math.floor(100 * scale),
};
return animate(
this.timer / mov[direction].length + 'ms',
style(applystyle)
);
});
....
});
}
This makes easer the .css that is only
.carousel{
display:flex;
margin-left:auto;
margin-right:auto;
margin-top:20px;
}
.carousel::after{
content:' ';
display: block;
clear: both;
}
.carousel>div{
float:left;
margin-right:-100%;
cursor:pointer;
}
.carousel>div img {
border-radius: 1rem;
box-shadow: 0 .5rem 1rem rgba(0,0,0,.35);
}
So the component are not so dependent for the sizes of the images. See also that I add a trasnformY to give a sensation to "view from above"
The changes in a new stackblitz
NOTE: By the moment, if we want to change the .css we can use the styles.css and write, e.g.
.custom .carousel>div img {
border-radius: 50%!important;
border:2px solid white;
}
Answered By - Eliseo
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.