129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
import {
|
|
AfterContentInit,
|
|
ChangeDetectorRef,
|
|
Component,
|
|
ContentChildren,
|
|
ElementRef,
|
|
Input, OnDestroy,
|
|
OnInit,
|
|
QueryList
|
|
} from "@angular/core";
|
|
import {SliderColumnComponent} from "./slider-column.component";
|
|
|
|
export class Stage {
|
|
value: number;
|
|
max: number;
|
|
}
|
|
|
|
@Component({
|
|
selector: 'slider-container',
|
|
template: `
|
|
<div class="uk-grid uk-grid-large uk-flex uk-flex-middle uk-flex-between uk-child-width-1-1" [ngClass]="'uk-child-width-1-' + sliders?.length + '@m'" uk-grid>
|
|
<ng-content></ng-content>
|
|
</div>
|
|
<div *ngIf="navigation === 'progress' && stages.length > 0" class="uk-flex uk-flex-center uk-margin-large-top">
|
|
<div class="uk-width-1-3@m uk-width-1-2@s uk-child-width-1-1 uk-grid uk-grid-small uk-flex-middle" [ngClass]="'uk-child-width-1-' + stages.length" uk-grid>
|
|
<div *ngFor="let stage of stages; let i=index">
|
|
<progress (click)="start(i)" class="uk-progress" [value]="stage.value" [max]="stage.max"></progress>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
})
|
|
export class SliderContainerComponent implements OnInit, OnDestroy, AfterContentInit {
|
|
private static INTERVAL = 10;
|
|
private static ANIMATION_DURATION = 600; // UIKit progress animation duration
|
|
|
|
@ContentChildren(SliderColumnComponent) sliders: QueryList<SliderColumnComponent>;
|
|
@Input()
|
|
navigation: 'progress' | null = null;
|
|
@Input()
|
|
total: number = 0;
|
|
@Input()
|
|
period: number = 3000; // in ms (>= 1000ms)
|
|
@Input()
|
|
infinite: boolean = false;
|
|
@Input()
|
|
parent: HTMLDivElement;
|
|
stages: Stage[] = [];
|
|
time: number = 0;
|
|
interval: any;
|
|
observer: IntersectionObserver;
|
|
initialized: boolean = false;
|
|
stopped: boolean = true;
|
|
|
|
constructor(private cdr: ChangeDetectorRef, private element: ElementRef) {
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.period = this.period - SliderContainerComponent.ANIMATION_DURATION;
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
if(this.observer) {
|
|
this.observer.disconnect();
|
|
}
|
|
}
|
|
|
|
ngAfterContentInit() {
|
|
this.setObserver();
|
|
this.sliders.forEach(slider => {
|
|
slider.setContainer(this);
|
|
slider.navItems.forEach(item => {
|
|
if(this.parent) {
|
|
item.background = getComputedStyle(this.parent).backgroundColor;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
setObserver() {
|
|
let options = {
|
|
root: null,
|
|
rootMargin: '0px',
|
|
threshold: 0.1
|
|
};
|
|
this.observer = new IntersectionObserver(entries => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting && !this.initialized) {
|
|
this.stopped = false;
|
|
this.start(0);
|
|
this.initialized = true;
|
|
} else {
|
|
this.initialized = false;
|
|
this.stopped = true;
|
|
}
|
|
});
|
|
}, options);
|
|
this.observer.observe(this.element.nativeElement);
|
|
}
|
|
|
|
start(time: number) {
|
|
this.stages = [];
|
|
for(let i = 0; i < this.total; i++) {
|
|
this.stages.push({value: (time > i?this.period:(time - i)), max: this.period});
|
|
}
|
|
if(this.interval) {
|
|
clearInterval(this.interval);
|
|
}
|
|
this.cdr.detectChanges();
|
|
this.interval = setInterval(() => {
|
|
let current = Math.floor(time);
|
|
this.stages[current].value += SliderContainerComponent.INTERVAL;
|
|
this.time = current + this.stages[current].value/this.stages[current].max;
|
|
this.sliders.forEach(slider => {
|
|
slider.change(this.time);
|
|
});
|
|
if(this.stages[current].value >= this.stages[current].max) {
|
|
clearInterval(this.interval);
|
|
let next = (current + 1 > this.total - 1)?0:current + 1;
|
|
if(!this.stopped && (this.infinite || next > current)) {
|
|
setTimeout(() => {
|
|
this.start(next);
|
|
}, SliderContainerComponent.ANIMATION_DURATION);
|
|
}
|
|
}
|
|
}, SliderContainerComponent.INTERVAL);
|
|
}
|
|
}
|