web-dev-qa-db-ja.com

Bootstrapマルチアイテムカルーセルの使用中にスライドが遅れるangular 6

私の問題は、Angular 6、Bootstrap v4とjQueryをマルチアイテムカルーセルに使用しています。[1,2,3、カルーセルの[4,5,6]は[1,2,3]を表示し、次を押すと[4,5,6]を表示する必要があります。

私が達成したこの部分まで、私は問題に直面しています。最初のアクティブカードだけが移動していて、遅れもあり、[前へ]ボタンと[次へ]ボタンは表示されません。 Stackblitzファイルを一度確認して、問題を理解してください

Stackblitzでの私の仕事です

https://stackblitz.com/edit/angular-jvr6dh

HTMLコード

<div class="container">
  <div id="dataInfo">
      <h2>Information</h2>
      <div id="myCarousel" class="carousel slide" data-ride="carousel">
          <div class="carousel-inner row w-100 mx-auto">
            <div class="carousel-item col-md-4 active">
              <div class="card">
                <img class="card-img-top img-fluid" src="https://images.freeimages.com/images/large-previews/85a/daisy-s-1375598.jpg"  width="100" height="100" alt="Card image cap">
                <div class="card-body">
                  <h4 class="card-title">Card 1</h4>
                  <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
                  <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
                </div>
              </div>
            </div>
            <div class="carousel-item col-md-4">
              <div class="card">

                <img class="card-img-top img-fluid" src="https://images.freeimages.com/images/large-previews/85a/daisy-s-1375598.jpg"  width="100" height="100" alt="Card image cap"><div class="card-body">
                  <h4 class="card-title">Card 2</h4>
                  <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
                  <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
                </div>
              </div>
            </div>
            <div class="carousel-item col-md-4">
              <div class="card">

                <img class="card-img-top img-fluid" src="https://images.freeimages.com/images/large-previews/85a/daisy-s-1375598.jpg"  width="100" height="100" alt="Card image cap"><div class="card-body">
                  <h4 class="card-title">Card 3</h4>
                  <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
                  <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
                </div>
              </div>
            </div>
            <div class="carousel-item col-md-4">
              <div class="card">

                <img class="card-img-top img-fluid" src="https://images.freeimages.com/images/large-previews/85a/daisy-s-1375598.jpg"  width="100" height="100" alt="Card image cap"><div class="card-body">
                  <h4 class="card-title">Card 4</h4>
                  <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
                  <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
                </div>
              </div>
            </div>
            <div class="carousel-item col-md-4">
              <div class="card">

                <img class="card-img-top img-fluid" src="https://images.freeimages.com/images/large-previews/85a/daisy-s-1375598.jpg"  width="100" height="100" alt="Card image cap"> <div class="card-body">
                  <h4 class="card-title">Card 5</h4>
                  <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
                  <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
                </div>
              </div>
            </div>
            <div class="carousel-item col-md-4">
              <div class="card">

                <img class="card-img-top img-fluid" src="https://images.freeimages.com/images/large-previews/85a/daisy-s-1375598.jpg"  width="100" height="100" alt="Card image cap"><div class="card-body">
                  <h4 class="card-title">Card 6</h4>
                  <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
                  <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
                </div>
              </div>
            </div>
            <div class="carousel-item col-md-4">
              <div class="card">

                <img class="card-img-top img-fluid" src="https://images.freeimages.com/images/large-previews/85a/daisy-s-1375598.jpg"  width="100" height="100" alt="Card image cap"><div class="card-body">
                  <h4 class="card-title">Card 7</h4>
                  <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
                  <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
                </div>
              </div>
            </div>
          </div>
          <a class="carousel-control-prev" href="#myCarousel" role="button" data-slide="prev">
            <span class="carousel-control-prev-icon" aria-hidden="true"></span>
            <span class="sr-only">Previous</span>
          </a>
          <a class="carousel-control-next" href="#myCarousel" role="button" data-slide="next">
            <span class="carousel-control-next-icon" aria-hidden="true"></span>
            <span class="sr-only">Next</span>
          </a>
        </div>

  </div>


</div>

。 tsコード:

$("#myCarousel").on("slide.bs.carousel", function(e) {
      var $e = $(e.relatedTarget);
      var idx = $e.index();
      var itemsPerSlide = 3;
      var totalItems = $(".carousel-item").length;

      if (idx >= totalItems - (itemsPerSlide - 1)) {
        var it = itemsPerSlide - (totalItems - idx);
        for (var i = 0; i < it; i++) {
          // append slides to end
          if (e.direction == "left") {
            $(".carousel-item")
              .eq(i)
              .appendTo(".carousel-inner");
          } else {
            $(".carousel-item")
              .eq(0)
              .appendTo($(this).find(".carousel-inner"));
          }
        }
      }
    });
  }

.scssコード

  .carousel-inner .active,
.carousel-inner .active + .carousel-item,
.carousel-inner .active + .carousel-item + .carousel-item {
  display: block;
}

.carousel-inner .carousel-item.active:not(.carousel-item-right):not(.carousel-item-left),
.carousel-inner .carousel-item.active:not(.carousel-item-right):not(.carousel-item-left) + .carousel-item,
.carousel-inner .carousel-item.active:not(.carousel-item-right):not(.carousel-item-left) + .carousel-item + .carousel-item {
  transition: none;
}

.carousel-inner .carousel-item-next,
.carousel-inner .carousel-item-prev {
  position: relative;
  transform: translate3d(0, 0, 0);
}

.carousel-inner .active.carousel-item + .carousel-item + .carousel-item + .carousel-item {
  position: absolute;
  top: 0;
  right: -33.3333%;
  z-index: -1;
  display: block;
  visibility: visible;
}

/* left or forward direction */
.active.carousel-item-left + .carousel-item-next.carousel-item-left,
.carousel-item-next.carousel-item-left + .carousel-item,
.carousel-item-next.carousel-item-left + .carousel-item + .carousel-item,
.carousel-item-next.carousel-item-left + .carousel-item + .carousel-item + .carousel-item {
  position: relative;
  transform: translate3d(-100%, 0, 0);
  visibility: visible;
}

/* farthest right hidden item must be abso position for animations */
.carousel-inner .carousel-item-prev.carousel-item-right {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  display: block;
  visibility: visible;
}

/* right or prev direction */
.active.carousel-item-right + .carousel-item-prev.carousel-item-right,
.carousel-item-prev.carousel-item-right + .carousel-item,
.carousel-item-prev.carousel-item-right + .carousel-item + .carousel-item,
.carousel-item-prev.carousel-item-right + .carousel-item + .carousel-item + .carousel-item {
  position: relative;
  transform: translate3d(100%, 0, 0);
  visibility: visible;
  display: block;
  visibility: visible;
}
10
Madpop

あなたのサンプルコードに基づいて、標準のカルーセルを使用するようにBootstrapコード自体を簡略化するように見えます。以下のリンクは、次の変更を加えて提供したサンプルのフォークです:

https://stackblitz.com/edit/angular-yaevix

  • Prev/next/etcのすべてのカスタムDOM操作を削除しました。あなたのjavascriptからBootstrapカルーセルコンポーネントを使用してすべてを実行しています
  • カードのアニメーションと配置を調整しようとしていたCSSを削除しました
  • 次/前のアイコンの迅速で汚れた配置を追加しました(これは、必要に応じてスタイルを設定できます)

各カードをカルーセルアイテムとして設定していますが、説明では一度に3ページずつページ分けしたいとします。ここでの正しいアプローチは、3枚のカードごとに1つのカルーセル項目を持つことです。以下の例を参照してください

複数のカードがある1つのカルーセル項目の例:

<div id="myCarousel" class="carousel slide" data-ride="carousel">
    <div class="carousel-inner">
        <div class="carousel-item active">
            <div class="row">
                <div class="col-md-4">
                    <div class="card">Card 1</div>
                </div>
                <div class="col-md-4">
                    <div class="card">Card 2</div>
                </div>
                <div class="col-md-4">
                    <div class="card">Card 3</div>
                </div>
            </div>
        </div>
        <div class="carousel-item active">
            <div class="row">
                <div class="col-md-4">
                    <div class="card">Card 4</div>
                </div>
                <div class="col-md-4">
                    <div class="card">Card 5</div>
                </div>
                <div class="col-md-4">
                    <div class="card">Card 6</div>
                </div>
            </div>
        </div>
        <div class="carousel-item">
            <div class="row">
                <div class="col-md-4">
                    <div class="card">Card 7</div>
                </div>
            </div>
        </div>
    </div>
</div>
7
inki

独自のカルーセル動作を記述し、jqueryへの依存を削除する方が簡単な場合があります。引き続きBootstrapを使用してスタイルを設定できます。

Angularアニメーションを使用して、アニメーションの動作を維持することもできます。(これが初めて使用したことに注意してくださいAngular =アニメーションなので、これを行うにはよりきちんとした方法があるかもしれません)

最初のステップは、next/prev動作を処理するディレクティブを作成することです。

  @Input() animationDuration = 500;

  private slideLeft: AnimationFactory;
  private slideRight: AnimationFactory;
  private slideInLeft: AnimationFactory;
  private slideInRight: AnimationFactory;

  constructor(private el: ElementRef, private _builder: AnimationBuilder) {
  }

  ngOnInit() {
    this.slideLeft = this._builder.build([
      style({ transform: 'translateX(0)' }),
      animate(this.animationDuration, style({ transform: 'translateX(-100%)' })),
      style({ transform: 'translateX(0)' }),
    ]);

    this.slideRight = this._builder.build([
      style({ transform: 'translateX(0)' }),
      animate(this.animationDuration, style({ transform: 'translateX(100%)' })),
      style({ transform: 'translateX(0)' }),
    ]);

    this.slideInLeft = this._builder.build([
      style({ transform: 'translateX(100%)', right: 0 }),
      animate(this.animationDuration, style({ transform: 'translateX(0)' })),
      style({ right: 'initial' })
    ]);

    this.slideInRight = this._builder.build([
      style({ transform: 'translateX(-100%)', left: 0 }),
      animate(this.animationDuration, style({ transform: 'translateX(0)' })),
      style({ left: 'initial' })
    ]);
  }

  next(steps) {
      let active = this.el.nativeElement.querySelectorAll('.carousel-item.active');
      let inactive = this.el.nativeElement.querySelector('.carousel-item:not(.active)');
      // Start the animation
      this.animateAll(active, this.slideLeft);
      // Start the slide in animation for the next element
      this.preMoveElement(inactive);
      this.slideInLeft.create(inactive).play();

      setTimeout(() => {
        // Move the last element to start and make it active.
        active = this.el.nativeElement.querySelectorAll('.carousel-item.active');
        inactive = this.el.nativeElement.querySelector('.carousel-item:not(.active)');
        active[0].classList.remove('active');
        this.el.nativeElement.insertBefore(active[0], null);
        inactive.classList.add('active');
        if (steps && steps - 1 > 0) {
          this.next(steps - 1);
        }
      }, this.animationDuration);
   // }
  }

  prev(steps) {
    const active = this.el.nativeElement.querySelectorAll('.carousel-item.active');
    const children = this.el.nativeElement.children;
    const lastChild = children[children.length - 1];
    // Start the animation
    this.animateAll(active, this.slideRight);
    // Start the slide in animation for the next element
    this.preMoveElement(lastChild);
    this.slideInRight.create(lastChild).play();


    setTimeout(() => {
      // Remove the active class
      const lastActive = active[active.length - 1];
      lastActive.classList.remove('active');
      // Move the last element to the start, and make it active
      this.el.nativeElement.insertBefore(lastChild, children[0]);
      lastChild.classList.add('active');
      if (steps && steps - 1 > 0) {
        this.prev(steps - 1);
      }
    }, this.animationDuration);
  }

  private animateAll(elements: any[], animation: AnimationFactory) {
    elements.forEach(element => {
      animation.create(element).play();
    });
  }

  private preMoveElement(element) {
    element.style.display = 'block';
    element.style.position = 'absolute';
    setTimeout(() => {
      element.style = null;
    }, this.animationDuration);
  }

次に、HTMLからprevおよびnext関数にアクセスできます。

変更 carousel-inner要素。

<div class="carousel-inner row w-100 mx-auto" #cara="Carousel">

次に、cara変数を使用してパブリック関数にアクセスできます。

  <a class="carousel-control-prev" role="button" (click)="cara.prev(3)">
    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
    <span class="sr-only">Previous</span>
  </a>
  <a class="carousel-control-next" role="button" (click)="cara.next(3)">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="sr-only">Next</span>
  </a>

最後に、carousel-items as .active、3つが同時に表示されるようにします。

ここにStackBlitzのデモがあります

4
user184994

この質問の長い時間ですが、私は別のアプローチをすることに抵抗することはできません。

免責事項私は偉大なネタネル基礎の4つのアイデアを盗み、 彼の驚くべきカルーセル

Disclamer 2長くて長い答えなので、アニメーションで読むには、 こちらです 取得するため

カルーセルは単なるhtmlです

<div class="wrap">
    <div #carousel></div>
</div>

div #carouselは、1つのアニメーションで左/右に移動します。

私が借りているネナテルの最初のアイデアはアニメーションです。それは2つの関数しかありません:(私は関数をパラメータ化するためにいくつかの変数を使用しています)

  @Input() timing = '500ms ease-in';
  slideWidth;    //width of each slide
  numberSlides;  //number of slides
  offset;        //a variable to move the origen of movimient

  private transitionCarousel(time: any, slide: number) {
    const offset = this.offset - this.slideWidth * this.numberSlides;
    const myAnimation: AnimationFactory = this.buildAnimation(offset, time);
    this.player = myAnimation.create(this.carousel.nativeElement);
    this.player.play();
  }

  private buildAnimation(offset, time: any) {
    return this.builder.build([
      animate(time == null ? this.timing : 0, style({ transform: `translateX(${offset}px)` }))
    ]);
  }

そこで、

   transitionCarousel(null,4)  //to select the slide 4.

ええと、私は6枚の異なる画像があり、同時に4枚を表示したいとします。重要な位置が2つあります。さまざまな州でカルーセルを鳴らす

 a)                            [0  1  2  3 ] 4  5  6
 b)                       0  1 [2  3  4  5 ] 6   
 c)                            [*  *  *  0 ]1  2  3  4  5  6
 d)           0  1  2  3  4  5 [6  *  *  * ]

つまり、a)はslide = 0の場合、b)はslide = 2の場合です。c)はスライド0が4番目の位置にある場合、d)はslide = 6の場合です。

明らかに、状態c)およびd)は、*空のespaceを表示します(そして空白スペースは必要ありません)。したがって、最初に画像4、5および6を繰り返し、最後に画像0 1および3を繰り返さないでください。したがって、状態c)およびd)は次のようになります。

 c)                            [4' 5' 6' 0 ] 1  2  3  4  5  6  0' 1' 2'
 d)  4' 5' 6' 0  1  2  3  4  5 [6  0' 1' 2']  

さて、ケースを考慮する必要がありますc)-slide = -3の場合、slide = 4と同じです

 c)        4' 5' 6' 0  1  2  3 [4  5  6  0']1' 2'

したがって、動きが0未満のスライドに落ちた場合、すぐに左に移動します。

考慮するもう1つのケースは、d)右ボタンをクリックしたときです。その場合、私たちは2つの動きをする必要があります

 d)  4' 5' 6' 0  1  2  3  4  5 [6  0' 1' 2']  //slide=6
                           --click left button event--
                           //we move to the slide=-1 before transition
                          4' 5'[6' 0  1  2 ] 3  4  5  6  0' 1' 2' 
                          //and move to slide=3 in transition 
              4' 5' 6' 0  1  2 [3  4  5  6 ] 0' 1' 2' 

したがって、transitionCarousel関数は次のようになります。

  private transitionCarousel(time: any, slide: number) {
    if (slide >= this.slides.length) {
        this.transitionCarousel(0, this.currentSlide - this.slidesQuantity)
        slide -= this.slidesQuantity;
    }

    const offset = this.offset - this.slideWidth * slide;
    const myAnimation: AnimationFactory = this.buildAnimation(offset, time);
    this.player = myAnimation.create(this.carousel.nativeElement);
    if (time != 0) {
      if (slide < 0) {
        this.player.onDone(() => {
          this.transitionCarousel(0, this.currentSlide)
        })
      }
      else
        this.currentSlide = slide;
    } this.player.play();
  }

さて、スライドを複製するには、いくつかのメカニズムが必要です。構造ディレクティブを使用してこれを行います(2番目はNetanel Basalに盗みました)

@Directive({
  selector: '[carouselItem]'
})
export class CarouselDirective implements OnInit {

  constructor(
    public templateRef: TemplateRef<any>,
    public viewContainer: ViewContainerRef) { }

  ngOnInit()
  {
    this.viewContainer.createEmbeddedView(this.templateRef)
  }
}

Publicとして宣言されているtemplateRefとviewContainerを参照してください。これにより、各要素のテンプレートをコピーできます

<div *carouselItem>Hello world</div>

別のものの中に。

カルーセルは次のようになります

<app-carousel #carousel>
  <ng-container *ngFor=" let item of items">
      <img *carouselItem [src]="item.img" >
  </ng-container>
</app-carousel>

ここで、例えば.

  items = [{ img: 'https://picsum.photos/200/200?random=1' },
  { img: 'https://picsum.photos/200/200?random=2' },
  { img: 'https://picsum.photos/200/200?random=3' },
  { img: 'https://picsum.photos/200/200?random=4' },
  { img: 'https://picsum.photos/200/200?random=5' },
  { img: 'https://picsum.photos/200/200?random=6' },
  { img: 'https://picsum.photos/200/200?random=7' },
  { img: 'https://picsum.photos/200/200?random=8' }
  ];

カルーセルをスケーラブルにしたいので、カルーセル内に保持できるスライドの数を知る必要があります。もちろん、カルーセルをdivで囲むことができます

<div style="width:600px">
    <app-carousel #carousel>
       ....
    </app-carousel>
</div>

そして、スライドの数をカルーセルに送信しますが、カルーセルが私たちのために機能させる方が良いです。スライドの幅を知る必要があります。ここで2番目の最後はネタネルに盗みました。 Netanelのアイデアは、.htmlのような

<div class="carousel-model">
    <ng-container [ngTemplateOutlet]="slides && slides.first?slides.first.templateRef:null">
    </ng-container>
</div>

次のようなクラスを選択するディレクティブを作成すると

@Directive({
  selector: '.carousel-model'
})
export class CarouselSlideElement {
}

そして、ViewChildを次のように定義しました

  @ViewChild(CarouselSlideElement, { static: false, read: ElementRef }) slideElement: ElementRef

ngAfterViewInitでは、

   this.slideElement.nativeElement.getBoundingClientRect()

寸法を取得する

まあ、私が「タイマー」を入れたのは、画像をロードしている場合、この画像の幅を忘れることがあるからです。

  ngAfterViewInit() {
    timer(0, 200).pipe(takeWhile(() => !this.slideWidth || !this.slides || !this.slides.first)).subscribe(() => {
      const square = this.slideElement.nativeElement.getBoundingClientRect();
      this.slideWidth = square.width;
      if (this.slideWidth && this.slides && this.slides.first)
        this.resizeCarousel()
    })
  }

もう一歩。より複雑な部分は、スライドを複製することです。 slides.firstは最初のスライドで、slides.lastは最後のスライドです。関数resizeCarouselですべて再描画します

  private resizeCarousel() {
    if (this.carousel) {
      let totalWidth = this.carousel.nativeElement.getBoundingClientRect().width;
      this.increment = Math.floor(totalWidth / this.slideWidth);

      let count = (this.increment * this.slideWidth) != totalWidth ? 1 : 0;
      this.offset = (totalWidth - 3 * (this.increment) * this.slideWidth) / 2 - this.slideWidth * count;
      console.log(totalWidth,count)
      this.slides.first.viewContainer.clear()
      this.slides.last.viewContainer.clear()

      this.slides.last.viewContainer.createEmbeddedView(this.slides.last.templateRef);
      let added = 0;
      this.slides.forEach((x, index) => {
        if (index && index >= (this.slides.length - this.increment - count)) {
          this.slides.first.viewContainer.createEmbeddedView(x.templateRef)
          added++
        }
        if (index < this.increment + count) {
          this.slides.last.viewContainer.createEmbeddedView(x.templateRef)
          added++
        }
      })
      this.slides.first.viewContainer.createEmbeddedView(this.slides.first.templateRef)
      this.currentSlide = 0;
      this.transitionCarousel(0, this.currentSlide);
    }
  }

画像が正確に合わない場合は、両側に画像を追加します

さて、ウィンドウのサイズを変更するためのhotListenerであるNetBasalに盗んだ最後の「借りるアイデア」

  @HostListener('window:resize', ['$event'])
  onResize(event) {
    if (this.slideWidth && this.slides && this.slides.first)
      this.resizeCarousel();
  }

そして、それはすべてのフォーク、次の3つの関数、prevおよびset、そして「listo el pollo」です。

  prev() {
    this.transitionCarousel(null, this.currentSlide + this.increment);
  }

  next() {
    this.transitionCarousel(null, this.currentSlide - this.increment);
  }

  setSlide(slide: number) {
    slide = slide;
    this.transitionCarousel(null, slide);
  }

Updateアニメーションを作成するためのより良いアプローチが使用されます

private transitionCarousel(時間:任意、スライド:数){

const myAnimation: AnimationFactory = this.buildAnimation(time,slide);
this.player = myAnimation.create(this.carousel.nativeElement);
this.currentSlide = (slide >= this.slides.length) ? slide - this.slides.length :
                    (slide < 0) ? this.currentSlide = slide + this.slides.length :
                     slide
this.player.play();

}

private buildAnimation(time:any、slide:number){const animation:number =(slide> = this.slides.length)?1:(slide <0)?2:0;

const offsetInitial = (slide >= this.slides.length)?
        this.offset - this.slideWidth * (this.currentSlide - this.slides.length):
        0;
let offsetFinal = (slide < 0)?
        this.offset - this.slideWidth * (slide + this.slides.length):
        0;

const offset = (slide >= this.slides.length)?
         this.offset - this.slideWidth * (slide-this.slides.length):
         this.offset - this.slideWidth * slide;

return animation==1 ? this.builder.build([
  style({ transform: `translateX(${offsetInitial}px)` }),
  animate(time == null ? this.timing : 0, style({ transform: `translateX(${offset}px)` }))
]) : animation==2 ? this.builder.build(sequence([
  animate(time == null ? this.timing : 0, style({ transform: `translateX(${offset}px)` })),
  style({ transform: `translateX(${offsetFinal}px` })]))
    : this.builder.build([
      animate(time == null ? this.timing : 0, style({ transform: `translateX(${offset}px)` }))
    ]);

}

1
Eliseo

これは単純な純粋なangularディレクティブの方法で、マルチアイテムカルーセルの実装です

ここでは、スライドのアイテム数を設定できます

これで単品カルーセルもできるといいな

https://github.com/amalroshan/angular-multiitem-carousel

0
amal