Search code examples
angularcarouselng-bootstrap

Angular Animate Ngbcarousel navigation indicators


I'm trying to animate Ngbcarousel navigation indicators (round dots indicating which slide the user is on) such that it scrolls to the right if the user clicks on, for example, the 5th indicator, and continues to scroll to the right if the user clicks on the 6th indicator onwards until it reaches the end.

I've combined the code from https://stackblitz.com/edit/ngu-carousel-ng6-7hxy9p?file=src/app/hello.component.ts with Ngbcarousel such that the navigation appears as dots.

Basically I got it to how I would like it to look like but I'm not sure how I can access Ngbcarousel dom structure and add animations to the ordered list (so when I press the 5th dot, the entire ordered list shifts to the left, showing more dots on the right)

Ideally, should look something like Instagram's carousel -> https://codepen.io/swarad07/pen/xmzQKm

carousel.component.html

<ngb-carousel
    #carousel
    class="profile-detail-carousel"
    [showNavigationArrows]="false"
    [showNavigationIndicators]="true"
    data-interval="false"
    (click)="navigateCarousel($event)"
  >
    <ng-template ngbSlide *ngFor="let imageID of imageIDs">
      <img class="carousel-img" [src]="profile | profileImage: imageID" />
    </ng-template>
    <ng-template ngbSlide *ngIf="!imageIDs?.length">
      <img class="carousel-img" [src]="{} | profileImage" />
    </ng-template>
</ngb-carousel>

caroursel.component.ts

navigateCarousel(event) {
    // checks what slideNumber was clicked
    const slideNumber = parseInt(event && event.path[0].id.match(/\d+/)[0], 10);
    if (slideNumber >= 5) {
      console.log(this.carousel); // carousel ol element to scroll right 
    }
  }

carousel.component.scss

.profile-detail-carousel .carousel-indicators {
  display: flex;
  justify-content: left;
  bottom: -40px !important;
  overflow: hidden;
  list-style-type: none;
  text-align: center;
  padding: 12px;
  margin: 0;
  width: 105px;
  white-space: nowrap;
  box-sizing: border-box;
  margin: auto;
  li {
    display: inline-block;
    border-radius: 50%;
    border: 2px solid rgba(0, 0, 0, 0.55);
    padding: 4px;
    margin: 0 3px;
    transition-timing-function: cubic-bezier(0.17, 0.67, 0.83, 0.67);
    transition: 0.4s;
    height: 1px;
    width: 1px;
    &.active {
      background: #6b6b6b;
      transform: scale(1.2);
    }
  }
}

Appreciate your help thanks!


Solution

  • puff are several, several thing to do.

    First animate the carousel, see this question in SO

    Second animate the indicators. if you want only show a few indicators, I thing that is better aproach create your own indicators

    <div class="wrapper">
    <div class="selectors" [@move]="{value: trans.value, params: {position:trans.position}}">
      <ng-container  *ngFor="let t of slidesValue">
            <div class="selector"  (click)="carousel.select(t)" [style.background-color]="carousel && t==carousel.activeId?'black':'transparent'">
            </div>
      </ng-container>
        </div>
    </div>
    

    I create an array "slidesValues" with the name of slides

    slidesValue=['id1','id2','id3','id4','id5','id6']
    

    Well, the animation i think is

    trigger('move', [
          state('true', style({ transform: 'translateX({{position}})' }), { params: { position: '10px' } }),
          state('false', style({ transform: 'translateX({{position}})' }), { params: { position: '10px' } }),
          transition('* <=>*', animate('260ms ease-in'))
    
        ])
    

    And in onSlide(event) I add (numIndicator are the indicator you can see, and a variable pos as the position)

    let index=this.slidesValue.findIndex(x=>x==event.current)
    if (index!=0 && index!=this.slidesValue.length-1)
    {
      if (index==this.pos+this.numIndicator-1)
        this.pos++;
      if (index==this.pos)
        this.pos--;
    
      this.trans={value:this.trans.value=!this.trans.value,position:-this.pos+'rem'}
    
    }
    

    I know is easy improve the code, but I left "as is" the final result in stackblitz

    Update Really I don't like whe way to control the "position". IT's better that the position depends from "index" only. And as the calculate is a bit comple, I prefer that the array slidesValue becomes an array of object with two properties "name" and "pos", so in ngOnInit

      ngOnInit()
      {
        let increment=this.numIndicator%2==0?0:.5
        let first=this.numIndicator/2-increment;
        let last=this.data.length-this.numIndicator/2-increment
        this.slidesValue=this.data.map((x,index)=>{
        return {
          name:'id'+index,
          pos:(index<first?first:index>last?last:index)
                  -this.numIndicator/2+increment
          }
        })
      }
    

    And the onSlide(event) becomes:

    this.pos=this.slidesValue.find(x=>x.name==event.current).pos
    this.trans={value:this.trans.value=!this.trans.value,position:-this.pos+'rem'}
    

    The stackBlitz is updated too