tweb/src/components/preloader.ts

140 lines
3.7 KiB
TypeScript
Raw Normal View History

import { isInDOM, cancelEvent } from "../lib/utils";
import { CancellablePromise } from "../helpers/cancellablePromise";
export default class ProgressivePreloader {
public preloader: HTMLDivElement;
private circle: SVGCircleElement;
private tempID = 0;
2020-04-14 17:46:31 +02:00
private detached = true;
private promise: CancellablePromise<any> = null;
2020-08-28 14:37:15 +02:00
constructor(elem?: Element, private cancelable = true, streamable = false) {
this.preloader = document.createElement('div');
this.preloader.classList.add('preloader-container');
2020-08-28 14:37:15 +02:00
if(streamable) {
this.preloader.classList.add('preloader-streamable');
}
this.preloader.innerHTML = `
<div class="you-spin-me-round">
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
2020-08-28 14:37:15 +02:00
<circle class="preloader-path-new" cx="50" cy="50" r="${streamable ? 19 : 23}" fill="none" stroke-miterlimit="10"/>
</svg>
</div>`;
if(cancelable) {
this.preloader.innerHTML += `
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-close" viewBox="0 0 20 20">
<line x1="0" y1="20" x2="20" y2="0" stroke-width="2" stroke-linecap="round"></line>
<line x1="0" y1="0" x2="20" y2="20" stroke-width="2" stroke-linecap="round"></line>
</svg>`;
} else {
this.preloader.classList.add('preloader-swing');
}
this.circle = this.preloader.firstElementChild.firstElementChild.firstElementChild as SVGCircleElement;
if(elem) {
this.attach(elem);
}
if(this.cancelable) {
this.preloader.addEventListener('click', (e) => {
cancelEvent(e);
if(this.promise && this.promise.cancel) {
this.promise.cancel();
this.detach();
}
});
}
}
2020-08-28 17:11:25 +02:00
public attachPromise(promise: CancellablePromise<any>) {
this.promise = promise;
2020-08-28 17:11:25 +02:00
const tempID = --this.tempID;
2020-08-28 17:11:25 +02:00
const onEnd = () => {
promise.notify = null;
2020-08-28 17:11:25 +02:00
if(tempID == this.tempID) {
this.detach();
this.promise = promise = null;
}
2020-08-28 17:11:25 +02:00
};
//promise.catch(onEnd);
promise.finally(onEnd);
if(promise.addNotifyListener) {
promise.addNotifyListener((details: {done: number, total: number}) => {
/* if(details.done >= details.total) {
onEnd();
} */
if(tempID != this.tempID) return;
//console.log('preloader download', promise, details);
const percents = details.done / details.total * 100;
this.setProgress(percents);
});
}
}
public attach(elem: Element, reset = true, promise?: CancellablePromise<any>, append = true) {
if(promise/* && false */) {
this.attachPromise(promise);
}
2020-04-14 17:46:31 +02:00
this.detached = false;
window.requestAnimationFrame(() => {
if(this.detached) return;
this.detached = false;
elem[append ? 'append' : 'prepend'](this.preloader);
if(this.cancelable && reset) {
this.setProgress(0);
}
2020-04-14 17:46:31 +02:00
});
}
public detach() {
2020-04-14 17:46:31 +02:00
this.detached = true;
2020-08-28 14:37:15 +02:00
//return;
if(this.preloader.parentElement) {
/* setTimeout(() => */window.requestAnimationFrame(() => {
2020-04-14 17:46:31 +02:00
if(!this.detached) return;
this.detached = true;
2020-04-25 03:17:50 +02:00
if(this.preloader.parentElement) {
this.preloader.remove();
2020-04-25 03:17:50 +02:00
}
})/* , 5e3) */;
}
}
public setProgress(percents: number) {
if(!isInDOM(this.circle)) {
return;
}
if(percents == 0) {
this.circle.style.strokeDasharray = '';
return;
}
2020-06-20 03:11:24 +02:00
try {
const totalLength = this.circle.getTotalLength();
2020-06-20 03:11:24 +02:00
//console.log('setProgress', (percents / 100 * totalLength));
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200';
} catch(err) {}
}
}