|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
import { Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
|
|
|
|
|
import { AfterViewInit, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, TemplateRef, ViewChild, ViewChildren } from '@angular/core';
|
|
|
|
|
import { BrowserModule } from '@angular/platform-browser';
|
|
|
|
|
import { Subject, debounceTime } from 'rxjs';
|
|
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'carousel',
|
|
|
|
@ -12,33 +13,187 @@ import { BrowserModule } from '@angular/platform-browser';
|
|
|
|
|
animations: [
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
export class CarouselComponent implements OnInit {
|
|
|
|
|
export class CarouselComponent implements OnInit, AfterViewInit {
|
|
|
|
|
|
|
|
|
|
@Output() afterInitSelf: EventEmitter<CarouselComponent> = new EventEmitter<CarouselComponent>();
|
|
|
|
|
@Input() slides?: any[];
|
|
|
|
|
@ContentChild(TemplateRef) template?: TemplateRef<any>;
|
|
|
|
|
|
|
|
|
|
@ViewChild('carouselWindow') window!: ElementRef;
|
|
|
|
|
|
|
|
|
|
@ViewChildren('slideElement') slideElements!: QueryList<ElementRef>;
|
|
|
|
|
|
|
|
|
|
current: number = 0;
|
|
|
|
|
intersectionObserver?: IntersectionObserver;
|
|
|
|
|
visibleSlides?: IntersectingSlide[];
|
|
|
|
|
increasing: boolean = true;
|
|
|
|
|
|
|
|
|
|
private debouncedOnchange: Subject<void> = new Subject<void>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(private cdr: ChangeDetectorRef) {
|
|
|
|
|
this.visibleSlides = [];
|
|
|
|
|
this.debouncedOnchange.pipe(debounceTime(500)).subscribe(() => this.realOnChange());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ngOnInit(): void {
|
|
|
|
|
this.afterInitSelf.next(this);
|
|
|
|
|
|
|
|
|
|
this.intersectionObserver = new IntersectionObserver((entries, observer) => {
|
|
|
|
|
entries.forEach(entry => {
|
|
|
|
|
const runIntersectionHandling = () => {
|
|
|
|
|
const entryIndex = parseInt(entry.target.getAttribute('data-slideIndex')!);
|
|
|
|
|
if (!entryIndex && entryIndex != 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const entryIntersectingSlide = this.visibleSlides?.find((s) => {
|
|
|
|
|
return s.index == entryIndex;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!entryIntersectingSlide) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entryIntersectingSlide.currentlyIntersecting = entry.isIntersecting;
|
|
|
|
|
|
|
|
|
|
if (entryIntersectingSlide.hasBeenVisible && !entry.isIntersecting) {
|
|
|
|
|
this.visibleSlides = this.visibleSlides?.filter(e => e.index != entryIndex);
|
|
|
|
|
this.intersectionObserver?.unobserve(entry.target);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
runIntersectionHandling();
|
|
|
|
|
this.onChange();
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngAfterViewInit(): void {
|
|
|
|
|
|
|
|
|
|
this.slideElements.changes.subscribe((comps: QueryList<ElementRef>) => {
|
|
|
|
|
comps.forEach((comp) => this.handleNewDomSlide(comp))
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.initialize();
|
|
|
|
|
this.cdr.detectChanges();
|
|
|
|
|
}, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
initialize() {
|
|
|
|
|
this.visibleSlides?.push({
|
|
|
|
|
index: 0,
|
|
|
|
|
hasBeenVisible: false,
|
|
|
|
|
currentlyIntersecting: false,
|
|
|
|
|
})
|
|
|
|
|
this.visibleSlides?.push({
|
|
|
|
|
index: 1,
|
|
|
|
|
hasBeenVisible: false,
|
|
|
|
|
currentlyIntersecting: false,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleNewDomSlide(ref: ElementRef) {
|
|
|
|
|
this.intersectionObserver?.observe(ref.nativeElement);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next(): void {
|
|
|
|
|
this.increasing = true;
|
|
|
|
|
if (this.slides && this.current + 1 < this.slides?.length) {
|
|
|
|
|
console.log("incremented");
|
|
|
|
|
this.current += 1;
|
|
|
|
|
|
|
|
|
|
if (this.current + 1 < this.slides.length) {
|
|
|
|
|
if (!this.visibleSlides?.find(e => e.index == this.current + 1)) {
|
|
|
|
|
this.visibleSlides?.push({
|
|
|
|
|
index: this.current + 1,
|
|
|
|
|
hasBeenVisible: false,
|
|
|
|
|
currentlyIntersecting: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.onChange();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prev(): void {
|
|
|
|
|
console.log("decremented");
|
|
|
|
|
this.increasing = false;
|
|
|
|
|
if (this.current - 1 >= 0) {
|
|
|
|
|
this.current -= 1;
|
|
|
|
|
|
|
|
|
|
if (this.current - 1 >= 0) {
|
|
|
|
|
if (!this.visibleSlides?.find(e => e.index == this.current - 1)) {
|
|
|
|
|
this.visibleSlides?.push({
|
|
|
|
|
index: this.current - 1,
|
|
|
|
|
hasBeenVisible: false,
|
|
|
|
|
currentlyIntersecting: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.onChange();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onChange() {
|
|
|
|
|
this.debouncedOnchange.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
realOnChange() {
|
|
|
|
|
{
|
|
|
|
|
const intersecting = this.visibleSlides?.filter(e => e.currentlyIntersecting).sort((e1, e2) => e1.index - e2.index);
|
|
|
|
|
const lowestIntersecting = intersecting![0];
|
|
|
|
|
console.log("lowest intersecting: ", lowestIntersecting?.index);
|
|
|
|
|
this.visibleSlides = this.visibleSlides?.filter(e => e.index + 2 >= lowestIntersecting!.index && e.index >= this.current - 2);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
const intersecting = this.visibleSlides?.filter(e => e.currentlyIntersecting).sort((e1, e2) => e1.index - e2.index).reverse();
|
|
|
|
|
const highestIntersecting = intersecting![0]
|
|
|
|
|
console.log("highest intersecting: ", highestIntersecting?.index);
|
|
|
|
|
this.visibleSlides = this.visibleSlides?.filter(e => e.index - 2 <= highestIntersecting!.index && e.index <= this.current + 2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
translation() {
|
|
|
|
|
let num = (this.current - 1) * (-1/3) * 100;
|
|
|
|
|
let num = (this.current - 1) * (-1 / 3) * 100;
|
|
|
|
|
return `${num}% 0`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
left(index: number) {
|
|
|
|
|
if (this.window.nativeElement) {
|
|
|
|
|
const width = this.window.nativeElement.clientWidth;
|
|
|
|
|
const slideWidth = width / 3.0;
|
|
|
|
|
const slideLeft = Math.round(index * slideWidth);
|
|
|
|
|
return `${slideLeft}px !important`;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// let num = (index) * 33.333333;
|
|
|
|
|
// return `${num}%`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
width(index: number) {
|
|
|
|
|
if (this.window.nativeElement) {
|
|
|
|
|
const width = this.window.nativeElement.clientWidth;
|
|
|
|
|
const slideWidth = Math.round(width / 3.0);
|
|
|
|
|
|
|
|
|
|
return `${slideWidth}px !important`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let num = 33.33333333;
|
|
|
|
|
return `${num}% !important`
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
templateColumns() {
|
|
|
|
|
const len = this.slides?.length;
|
|
|
|
|
const value = `repeat(${len}, minmax(calc(100% / 3), 1fr))`;
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface IntersectingSlide {
|
|
|
|
|
index: number;
|
|
|
|
|
hasBeenVisible: boolean;
|
|
|
|
|
currentlyIntersecting: boolean;
|
|
|
|
|
}
|