183 lines
5.3 KiB
JavaScript
183 lines
5.3 KiB
JavaScript
const MszLoadingIcon = function() {
|
|
const element = <div class="msz-loading-icon"/>;
|
|
for(let i = 0; i < 9; ++i)
|
|
element.appendChild(<div class="msz-loading-icon-block"/>);
|
|
|
|
// this is moderately cursed but it'll do
|
|
const blocks = [
|
|
element.children[3],
|
|
element.children[0],
|
|
element.children[1],
|
|
element.children[2],
|
|
element.children[5],
|
|
element.children[8],
|
|
element.children[7],
|
|
element.children[6],
|
|
];
|
|
|
|
let tsLastUpdate;
|
|
let counter = 0;
|
|
let playing = false;
|
|
let delay = 50;
|
|
let playResolve;
|
|
let pauseResolve;
|
|
|
|
const update = tsCurrent => {
|
|
try {
|
|
if(tsLastUpdate !== undefined && (tsCurrent - tsLastUpdate) < delay)
|
|
return;
|
|
tsLastUpdate = tsCurrent;
|
|
|
|
for(let i = 0; i < blocks.length; ++i)
|
|
blocks[(counter + i) % blocks.length].classList.toggle('msz-loading-icon-block-hidden', i < 3);
|
|
|
|
++counter;
|
|
} finally {
|
|
if(playResolve)
|
|
try {
|
|
playResolve();
|
|
} finally {
|
|
playResolve = undefined;
|
|
playing = true;
|
|
}
|
|
|
|
if(pauseResolve)
|
|
try {
|
|
pauseResolve();
|
|
} finally {
|
|
pauseResolve = undefined;
|
|
playing = false;
|
|
}
|
|
|
|
if(playing)
|
|
requestAnimationFrame(update);
|
|
}
|
|
};
|
|
|
|
const play = () => {
|
|
return new Promise(resolve => {
|
|
if(playing || playResolve) {
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
playResolve = resolve;
|
|
requestAnimationFrame(update);
|
|
});
|
|
};
|
|
const pause = () => {
|
|
return new Promise(resolve => {
|
|
if(!playing || pauseResolve) {
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
pauseResolve = resolve;
|
|
});
|
|
};
|
|
const stop = async () => {
|
|
await pause();
|
|
counter = 0;
|
|
};
|
|
const restart = async () => {
|
|
await stop();
|
|
await play();
|
|
};
|
|
const reverse = () => {
|
|
blocks.reverse();
|
|
};
|
|
const setBlock = (num, state=null) => {
|
|
element.children[num].classList.toggle('msz-loading-icon-block-hidden', !state);
|
|
};
|
|
const batsu = () => {
|
|
setBlock(0, true);setBlock(1, false);setBlock(2, true);
|
|
setBlock(3, false);setBlock(4, true);setBlock(5, false);
|
|
setBlock(6, true);setBlock(7, false);setBlock(8, true);
|
|
};
|
|
const maru = () => {
|
|
setBlock(0, true);setBlock(1, true);setBlock(2, true);
|
|
setBlock(3, true);setBlock(4, false);setBlock(5, true);
|
|
setBlock(6, true);setBlock(7, true);setBlock(8, true);
|
|
};
|
|
|
|
return {
|
|
get element() { return element; },
|
|
get playing() { return playing; },
|
|
get delay() { return delay; },
|
|
set delay(value) {
|
|
if(typeof value !== 'number')
|
|
value = parseFloat(value);
|
|
if(isNaN(value) || !isFinite(value))
|
|
return;
|
|
if(value < 0)
|
|
value = Math.abs(value);
|
|
delay = value;
|
|
},
|
|
|
|
play,
|
|
pause,
|
|
stop,
|
|
restart,
|
|
reverse,
|
|
batsu,
|
|
maru,
|
|
};
|
|
};
|
|
|
|
const MszLoading = function(options=null) {
|
|
if(typeof options !== 'object')
|
|
throw 'options must be an object';
|
|
|
|
let {
|
|
element, size, colour,
|
|
width, height, inline,
|
|
containerWidth, containerHeight,
|
|
gap, margin, hidden,
|
|
} = options ?? {};
|
|
|
|
if(typeof element === 'string')
|
|
element = document.querySelector(element);
|
|
if(!(element instanceof HTMLElement))
|
|
element = <div class="msz-loading"/>;
|
|
|
|
if(!element.classList.contains('msz-loading'))
|
|
element.classList.add('msz-loading');
|
|
if(inline)
|
|
element.classList.add('msz-loading-inline');
|
|
if(hidden)
|
|
element.classList.add('hidden');
|
|
|
|
if(typeof size === 'number' && size > 0)
|
|
element.style.setProperty('--msz-loading-size', size);
|
|
if(typeof containerWidth === 'string')
|
|
element.style.setProperty('--msz-loading-container-width', containerWidth);
|
|
if(typeof containerHeight === 'string')
|
|
element.style.setProperty('--msz-loading-container-height', containerHeight);
|
|
if(typeof gap === 'string')
|
|
element.style.setProperty('--msz-loading-gap', gap);
|
|
if(typeof margin === 'string')
|
|
element.style.setProperty('--msz-loading-margin', margin);
|
|
if(typeof width === 'string')
|
|
element.style.setProperty('--msz-loading-width', width);
|
|
if(typeof height === 'string')
|
|
element.style.setProperty('--msz-loading-height', height);
|
|
if(typeof colour === 'string')
|
|
element.style.setProperty('--msz-loading-colour', colour);
|
|
|
|
let icon;
|
|
if(element.childElementCount < 1) {
|
|
icon = new MszLoadingIcon;
|
|
icon.play();
|
|
element.appendChild(<div class="msz-loading-frame">{icon}</div>);
|
|
}
|
|
|
|
return {
|
|
get element() { return element; },
|
|
|
|
get hasIcon() { return icon !== undefined; },
|
|
get icon() { return icon; },
|
|
|
|
get visible() { return !element.classList.contains('hidden'); },
|
|
set visible(state) { element.classList.toggle('hidden', !state); },
|
|
};
|
|
};
|