finished initial version of carousel. Might look into getting rid of inconsisten ~1pixel gaps sometimes showing up between slides

merge-notes
isark 2 years ago
parent ab35c9e667
commit b101524105

@ -1,14 +1,18 @@
<ng-container *ngIf="template && slides">
<div class="carousel">
<div class="window" [style.translate]="translation()">
<ng-container *ngFor="let slide of slides">
<div class="slide"><ng-container
*ngTemplateOutlet="template!; context: { $implicit: slide }"></ng-container></div>
<div class="window" [style.translate]="translation()" #carouselWindow [style.grid-template-columns]="templateColumns()">
<span hidden>current: {{current}}</span>
<ng-container *ngFor="let visible of visibleSlides">
<div class="slide" #slideElement [attr.data-slideIndex]="visible.index" [style.grid-column-start]="visible.index + 1">
<ng-container *ngTemplateOutlet="template!; context: { $implicit: slides[visible.index] }">
</ng-container>
</div>
</ng-container>
</div>
</div>
<button (click)="prev()">&lt;=prev</button>
<button (click)="next()">next&gt;</button>
<div class="controls">
<button (click)="prev()">&lt;=prev</button>
<button (click)="next()">next&gt;</button>
</div>
</ng-container>

@ -6,21 +6,25 @@
.carousel {
display: flex;
flex-direction: row;
height: 100%;
}
.slide {
width: 33.333%;
min-width: 33.333%;
max-width: 33.333%;
background-color: green;
grid-template-rows: 1;
}
.controls {
position: absolute;
top: 10%;
}
.window {
flex: 1 1 auto;
display: flex;
flex-direction: row;
width: 100%;
transition: 1s;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-flow: column;
width: 100%;
}
.current {

@ -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;
}
Loading…
Cancel
Save