import { Component, OnInit, QueryList, ContentChildren, Input, Output, HostBinding, HostListener, EventEmitter, ElementRef } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; import 'rxjs/add/operator/throttleTime'; import { UICarouselItemComponent } from '../ui-carousel-item/ui-carousel-item.component'; @Component({ selector: 'ui-carousel', template : `
`, styles: [` :host{ display: block; overflow: hidden; position: relative; } `], }) export class UICarouselComponent implements OnInit { private nextSubject: Subject = new Subject(); private prevSubject: Subject = new Subject(); @Output() onChange: EventEmitter = new EventEmitter(); @Input() height: string = "300px"; @Input() width: string = "100%"; @Input() speed: number; @Input() autoPlay: boolean = true; @Input() autoPlaySpeed: number; @Input() infinite: boolean = true; @Input() fade: boolean = false; @Input('dots') isDotsVisible: boolean = true; @Input('arrows') isArrowsVisible: boolean = true; @ContentChildren(UICarouselItemComponent) items: QueryList; private _width: number; currentItemIndex: number = 0; interval:any; private firstItemIndex: number; // the visual index of item and not necessary the index in the DOM private lastItemIndex: number; // .. constructor( private el: ElementRef ) { } ngOnInit() { this.speed = this.speed || 500; this.autoPlaySpeed = this.autoPlaySpeed || 1500; if(this.autoPlay){ this.autoPlayFunction(true); } this.nextSubject.throttleTime(this.speed).subscribe(() => { if (!this.fade) { this.slideLeft(); } else { this.fadeLeft(); } }); this.prevSubject.throttleTime(this.speed).subscribe(() => { if (!this.fade) { this.slideRight(); } else { this.fadeRight(); } }); this.onChange.subscribe((index: number) => { let item = this.getItemByIndex(index); item.lazyLoad(); }) } ngAfterViewInit() { this.el.nativeElement.style.height = this.height; this.el.nativeElement.style.width = this.width; if (this.items && this.items.length > 0) { this.onChange.emit(0); this._width = this.items.first.el.nativeElement.offsetWidth; } this.firstItemIndex = 0; this.lastItemIndex = this.items.length - 1; if (!this.fade) { this.items.forEach((item, itemIndex) => { let totalDistanceSwiped = 0; item.speed = this.speed; item.position = this._width * itemIndex; item.currentPosition = item.position; item.disableTransition(); item.moveTo(item.position); item.swiper.onSwipeLeft.subscribe((distance: number) => { totalDistanceSwiped += Math.abs(distance); let shortDistance = distance / Math.pow(totalDistanceSwiped, .4); if (itemIndex === this.firstItemIndex && this.infinite) { this.rotateRight(); } this.items.forEach((itm, index) => { if ((itemIndex === this.firstItemIndex || (itemIndex === this.lastItemIndex && distance > 0)) && !this.infinite) { itm.currentPosition += shortDistance; } else { itm.currentPosition += distance; } itm.moveTo(itm.currentPosition); }); }); item.swiper.onSwipeRight.subscribe((distance: number) => { totalDistanceSwiped += Math.abs(distance); let shortDistance = distance / Math.pow(totalDistanceSwiped, .4); if (itemIndex === this.lastItemIndex && this.infinite) { this.rotateLeft(); } this.items.forEach((itm, index) => { if ((itemIndex === this.lastItemIndex || (itemIndex === this.firstItemIndex && distance < 0)) && !this.infinite) { itm.currentPosition += shortDistance; } else { itm.currentPosition += distance; } itm.moveTo(itm.currentPosition); }); }); item.swiper.swipeLeft.subscribe(() => { totalDistanceSwiped = 0; this.slideLeft(); }); item.swiper.swipeRight.subscribe(() => { totalDistanceSwiped = 0; this.slideRight(); }); item.swiper.onSwipeEnd.subscribe(() => { totalDistanceSwiped = 0; this.enableTransition(); this.slideToPrevPosition(); }); item.swiper.onSwipeStart.subscribe(() => { totalDistanceSwiped = 0; this.disableTransition(); }); }); } else { this.items.forEach((item, index) => { item.zIndex = this.items.length - index; item.setzIndex(item.zIndex); }); } } next() { this.prevSubject.next(); } prev() { this.nextSubject.next(); } goTo(index: number) { if (!this.fade) { this.slideTo(index); } else { this.fadeTo(index); } } rotateRightTo(index: number) { while (index !== this.lastItemIndex) { this.rotateRight(); } } rotateLeftTo(index: number) { while (index !== this.firstItemIndex) { this.rotateLeft(); } } slideTo(index: number) { this.onChange.emit((index + this.items.length) % this.items.length); let steps = this.currentItemIndex - index; if (this.infinite) { if (steps > 0) { this.rotateRightTo(this.currentItemIndex); } else if (steps < 0) { this.rotateLeftTo(this.currentItemIndex); } } setTimeout(() => { this.enableTransition(); this.items.forEach((item, i) => { item.position += this._width * (steps); item.currentPosition = item.position; item.moveTo(item.position); }); this.currentItemIndex = (index + this.items.length) % this.items.length; }, 50); } slideLeft() { if (!this.infinite) { if (this.currentItemIndex === 0) { this.slideToPrevPosition(); return; } } this.slideTo(this.currentItemIndex - 1); } slideRight() { if (!this.infinite) { if (this.currentItemIndex === this.items.length - 1) { this.slideToPrevPosition(); return; } } this.slideTo(this.currentItemIndex + 1); } slideToPrevPosition() { this.enableTransition(); this.items.forEach(item => { item.currentPosition = item.position; item.moveTo(item.position); }) } disableTransition() { this.items.forEach((item, index) => { item.disableTransition() }) } enableTransition() { this.items.forEach((item, index) => { item.enableTransition() }); } getItemByIndex(index: number) { return this.items.find((item, i) => { return i === index; }); } getIndexByItem(item: UICarouselItemComponent) { return this.items.toArray().indexOf(item); } rotateRightNTimes(n: number) { for (let i = 0; i < n; i++) { this.rotateRight(); } } rotateLeftNTimes(n: number) { for (let i = 0; i < n; i++) { this.rotateLeft(); } } rotateRight() { let firstItemRef = this.getItemByIndex(this.firstItemIndex); let lastItemRef = this.getItemByIndex(this.lastItemIndex); if (!this.fade) { lastItemRef.position = firstItemRef.position - this._width; lastItemRef.currentPosition = lastItemRef.position; lastItemRef.disableTransition(); lastItemRef.moveTo(lastItemRef.position); this.firstItemIndex = this.lastItemIndex; this.lastItemIndex = (this.lastItemIndex - 1 + this.items.length) % this.items.length; } } rotateLeft() { let firstItemRef = this.getItemByIndex(this.firstItemIndex); let lastItemRef = this.getItemByIndex(this.lastItemIndex); firstItemRef.position = lastItemRef.position + this._width; firstItemRef.currentPosition = firstItemRef.position; firstItemRef.disableTransition(); firstItemRef.moveTo(firstItemRef.position); this.lastItemIndex = this.firstItemIndex; this.firstItemIndex = (this.lastItemIndex + 1) % this.items.length; } fadeTo(index: number) { this.onChange.emit(index); let firstItem = this.getItemByIndex(this.currentItemIndex); let targetItem = this.getItemByIndex(index); let highestZIndex = this.items.length; targetItem.zIndex = firstItem.zIndex + 1; targetItem.setzIndex(targetItem.zIndex); targetItem.disableTransition(); targetItem.fadeIn(this.speed); this.currentItemIndex = index; } fadeRight() { let newIndex = (this.currentItemIndex + 1) % this.items.length; this.fadeTo(newIndex) this.currentItemIndex = newIndex; } fadeLeft() { let newIndex = (this.currentItemIndex - 1 + this.items.length) % this.items.length; this.fadeTo(newIndex); this.currentItemIndex = newIndex; } // is item first visually and not necessary first in the dom (QueryList) isItemFirst(index: number) { return this.firstItemIndex === index; } // is item last visually and not necessary last in the dom (QueryList) isItemLast(index: number) { return this.lastItemIndex === index; } @HostListener('window:resize', ['$event']) onResize(event: any) { this.rePosition(); } rePosition() { if (this.items && this.items.length > 0) { this._width = this.items.first.el.nativeElement.offsetWidth; } let items = this.items.toArray(); items.sort((item1, item2) => { if (item1.position > item2.position) return 1; else if (item1.position < item2.position) return -1; else return 0; }) let currentItem = this.getItemByIndex(this.currentItemIndex); let currentItemIndex = items.indexOf(currentItem); for (let i = currentItemIndex; i < items.length + currentItemIndex; i++) { let item = items[(i + items.length) % items.length]; item.position = ((i + items.length) % items.length - currentItemIndex) * this._width; item.disableTransition(); item.moveTo(item.position); } } ngOnDestroy(){ this.nextSubject.unsubscribe(); this.prevSubject.unsubscribe(); } autoPlayFunction(isAutoPlay : boolean){ if(this.autoPlay){ if(isAutoPlay){ this.interval = setInterval(()=>{ this.next(); }, this.autoPlaySpeed); }else{ clearInterval(this.interval); } } } }