私の問題は、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;
}
あなたのサンプルコードに基づいて、標準のカルーセルを使用するようにBootstrapコード自体を簡略化するように見えます。以下のリンクは、次の変更を加えて提供したサンプルのフォークです:
https://stackblitz.com/edit/angular-yaevix
各カードをカルーセルアイテムとして設定していますが、説明では一度に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>
独自のカルーセル動作を記述し、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-item
s as .active
、3つが同時に表示されるようにします。
この質問の長い時間ですが、私は別のアプローチをすることに抵抗することはできません。
免責事項私は偉大なネタネル基礎の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)` }))
]);
}
これは単純な純粋なangularディレクティブの方法で、マルチアイテムカルーセルの実装です
ここでは、スライドのアイテム数を設定できます
これで単品カルーセルもできるといいな