user3653474
user3653474

Reputation: 3854

Custom animated image slider in Angular

I want to create a custom image slider, where i can load images dynamically, This is a link of working example:

https://stackblitz.com/edit/angular-ivy-rwuxjd?file=src/app/app.component.html

Slider is working but there is one issue i want to hide current slide and translate incoming slide only. New slide should translate from extreme right. But in this code current slide is also translating.

Upvotes: 1

Views: 2816

Answers (1)

Eliseo
Eliseo

Reputation: 57919

Sometime ago I used the ngb-carousel and animate to left-rigth and rigth-left (it's time ngb-carousel has no animation) in this SO (the second update) Since 8.0.0 you has animation.

I feel you can not use :enter and :left because only when enter the imagen is loaded. So, based in the before SO, you can define an animation like

const animation = [
    state('outright', style({ transform: `translateX(100%)` })),
    state('outleft', style({ transform: `translateX(-100%)` })),
    transition('void=>inleft',[
      style({transform:`translateX(0)`}),
    ]),
    transition('void=>outleft',[
      style({transform:`translateX(-100%)`}),
    ]),

    transition('*=>inright',[
      style({transform:`translateX(-100%)`}),
      animate('260ms ease-in',style({ transform: `translateX(0)` }))
    ]),
    transition('*=>inleft',[
      style({transform:`translateX(100%)`}),
      animate('260ms ease-in',style({ transform: `translateX(0)` }))
    ]),
    transition('*=>outleft', [
      animate('260ms ease-in', style({ transform: `translateX(-100%)` }))
    ]),
    transition('*=>outright', [
      animate('260ms ease-in',style({ transform: `translateX(100%)` }))
    ]),
]

see that a slider can be outleft,outrigth,inleft or inrigth

So I define an array like

slideControl: any[] = this.images.map((x,index)=>index ? 'outleft' : 'inleft')

And when we click next or prev we call to the function onSlide

  onNext() {
    if (this.counter != this.images.length - 1) {
      this.counter++;
      this.onSlide(this.counter,this.counter-1,'right')
    }
    
  }

  onPrevious() {
    if (this.counter > 0) {
      this.counter--;
      this.onSlide(this.counter,this.counter+1,'left')
    }
  }
  onSlide(current,prev,direction) {
    this.slideControl=this.slideControl.map((x, index) => {
      return (index == current) ?'in' + direction :
             (index == prev) ? 'out' + direction : x
    })
  }

The last piece of jigsaw are the .html

<div class="wrapper" >
  <div *ngFor="let img of images; let i = index" 
          [@animImageSlider]="slideControl[i]">
    <img #slide [src]="img" style="height:200px; width:200px"  />
  </div>
</div>

And the .css, See that this animation work if all the images are placed in the position top:0, left:0 of the wrapper

.wrapper{
  overflow: hidden;
  width:200px;
}
.wrapper::after {
  display: block;
  clear: both;
  content: "";
}
.wrapper div{
  float:left;
  margin-right: -100%;
}

the stackblitz

Update Well, the function onSlide can be simply:

  onSlide(current,prev,direction) {
    this.slideControl[current]='in' + direction;
    this.slideControl[prev]='out' + direction
  }

And there're a point I don't like that it's the "hardcode" with of wrapper. We can take advantage of the event load and use a variable with in the way

  width:number=0;
  onLoad(el:any)
  {
    this.width=el.getBoundingClientRect().width
  }

So, our .html can be like

<div class="wrapper" [style.width.px]="width" >
  <div *ngFor="let img of images; let i = index" 
              [@animImageSlider]="slideControl[i]">
    <img #slide (load)="i==0 && onLoad(slide)" [src]="img" />
  </div>
</div>

See that the function "onLoad" it's only called for the first slide

NOTE: If we want a "infinite carousel" we can change reemplace the functions next and prev by

  change(direction:string)
  {
    const incr=direction=='right'?1:-1
    const prev=this.counter
    this.counter=(this.counter+this.images.length+incr)%this.images.length
    this.slideControl[this.counter]='in' + direction;
    this.slideControl[prev]='out' + direction
  }

And use

<button type="button" (click)="change('left')" >
  Previous
</button>
<button type="button" (click)="change('right')">
  Next
</button>

NOTE: I update the stackblitz with this two changes

Upvotes: 1

Related Questions