feat: Add loader step

- Add loader element with progress display
- Add styles for loader, spinner and bar
- Move event listeners for hint to intro step
- Add loadCore function to simulate loader
- Keep alternative progress bar for document loading display
This commit is contained in:
sthag 2025-05-03 12:34:23 +02:00
parent 2c001aaa4e
commit 7255b009b9
3 changed files with 196 additions and 11 deletions

View file

@ -28,17 +28,17 @@ const hint = {
clearTimeout(this.timeoutId); clearTimeout(this.timeoutId);
} }
}; };
const loader = document.getElementById('loader');
window.addEventListener('click', () => hint.show());
window.addEventListener('keydown', () => hint.show());
function agree() { function agree() {
if (agreement) { if (agreement) {
console.info("Agreement show."); console.info("Agreement show.");
agreement.classList.replace('op_hide', 'op_show'); agreement.classList.replace('op_hide', 'op_show');
setTimeout(() => { setTimeout(() => {
agreement.classList.replace('op_show', 'op_hide'); agreement.classList.replace('op_show', 'op_hide');
console.info("Agreement closed."); console.info("Agreement closed.");
}, introDelay * 1000) }, introDelay * 1000)
} }
@ -47,6 +47,7 @@ function agree() {
function init() { function init() {
return new Promise((resolve) => { return new Promise((resolve) => {
console.log('Init'); console.log('Init');
resolve(); resolve();
}); });
} }
@ -55,17 +56,20 @@ function showIntro() {
const el = intro; const el = intro;
const dy = introDelay * 1000; const dy = introDelay * 1000;
window.addEventListener('click', () => hint.show());
window.addEventListener('keydown', () => hint.show());
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (el) { if (el) {
console.info("Intro begin."); console.info("Intro begin.");
el.classList.replace('op_hide', 'op_show');
el.classList.replace('op_hide', 'op_show');
setTimeout( setTimeout(
() => { () => {
el.classList.replace('op_show', 'op_hide'); el.classList.replace('op_show', 'op_hide');
el.addEventListener('transitionend', () => { el.addEventListener('transitionend', () => {
console.info("Intro fin."); console.info("Intro fin.");
resolve("Intro fin."); resolve("Intro fin.");
}); });
}, },
@ -84,14 +88,14 @@ function showAgreement() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (el) { if (el) {
console.info("Agreement show."); console.info("Agreement show.");
el.classList.replace('op_hide', 'op_show');
el.classList.replace('op_hide', 'op_show');
setTimeout( setTimeout(
() => { () => {
el.classList.replace('op_show', 'op_hide'); el.classList.replace('op_show', 'op_hide');
el.addEventListener('transitionend', () => { el.addEventListener('transitionend', () => {
console.info("Agreement closed."); console.info("Agreement closed.");
resolve("closed"); resolve("closed");
}); });
}, },
@ -103,11 +107,115 @@ function showAgreement() {
}) })
} }
function loadCore() {
const el = loader;
const bar = loader.querySelector('#progress');
const status = loader.querySelector('#status');
const spinner = loader.querySelector('#spinner');
const sp = spinner.querySelector('span');
let progress = 0;
return new Promise((resolve) => {
console.info("Core loading.");
updateProgressBar();
function updateProgressBar() {
let increment = randomIntFrom(1, 19);
progress += increment;
if (progress >= 100) progress = 100;
// console.log(progress);
bar.style.width = progress + '%';
status.textContent = progress + '%';
if (progress < 100) {
setTimeout(updateProgressBar, 800);
} else {
bar.style.width = '100%';
sp.style.animationPlayState = 'paused';
spinner.style.color = 'white';
spinner.style.backgroundColor = 'black';
el.classList.replace('op_show', 'op_hide');
el.addEventListener('transitionend', () => {
console.info("Core loaded.");
resolve("Core loaded.");
});
}
}
});
}
init() init()
.then(loadCore)
.then(showIntro) .then(showIntro)
.catch(er => console.error(er)) .catch(er => console.error(er))
.then(showAgreement) .then(showAgreement)
.catch(er => console.error(er)) .catch(er => console.error(er))
.finally(() => { .finally(() => {
console.debug('Init end.'); console.debug('Init end.', loader, intro, agreement);
}); });
// document.addEventListener('DOMContentLoaded', () => {
// const barEl = document.getElementById('bar');
// const bar = document.getElementById('progress');
// const status = document.getElementById('status');
// const spinnerEl = document.getElementById('spinner');
// const spinner = document.getElementById('spinner').querySelector('span');
// let progress = 0;
// function updateProgressBar() {
// let increment = randomIntFrom(1, 9);
// progress += increment;
// if (progress >= 100) progress = 100;
// console.log(progress);
// bar.style.width = progress + '%';
// status.textContent = progress + '%';
// if (progress < 100) {
// setTimeout(updateProgressBar, 100);
// } else {
// bar.style.width = '100%';
// spinner.style.animationPlayState = 'paused';
// spinnerEl.style.color = 'white';
// spinnerEl.style.backgroundColor = 'black';
// }
// }
// updateProgressBar();
// window.addEventListener('load', () => {
// // progressEl.style.width = '100%';
// // setTimeout(() => {
// // progressBar.style.opacity = 0;
// // setTimeout(() => {
// // progressBar.style.display = 'none';
// // }, 500);
// // }, 2000);
// });
// });
/**
* Gibt eine Zahl zwischen <min> und <max> aus.
* Die Werte <min> und <max> sind dabei mit eingeschlossen.
* Mit <pos> kann der Exponent für eine 10er-Teilung angegeben werden.
*
* @param {number} min
* @param {number} max
* @param {number} pos
* @returns {number}
*/
function randomIntFrom(min, max, pos = 0) {
pos = Math.pow(10, pos);
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor((Math.random() * (max - min + 1) + min) / pos) * pos;
}

View file

@ -22,6 +22,15 @@ tags:
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<div id="loader" class="op_show">
<div id="bar">
<div id="spinner"><span>I</span></div>
<div id="wrap">
<div id="progress"></div>
</div>
<div id="status">0%</div>
</div>
</div>
<div id="hint" class="toast di_none" role="alert" aria-live="assertive" aria-atomic="true"> <div id="hint" class="toast di_none" role="alert" aria-live="assertive" aria-atomic="true">
<p>Hold <kbd>space</kbd> to skip.</p> <p>Hold <kbd>space</kbd> to skip.</p>
</div> </div>

View file

@ -34,6 +34,74 @@ $z-indexes: (
} }
} }
#loader {
@extend %full_parent;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: white;
}
#bar {
display: flex;
justify-content: space-between;
width: 50%;
}
#wrap {
flex: 1;
background-color: $color_back_basic;
}
#progress {
width: 0%;
height: 100%;
background-color: black;
}
#status,
#spinner {
@extend %basic;
display: flex;
flex-grow: 0;
flex-shrink: 0;
justify-content: center;
align-items: center;
margin-inline: $space_half;
padding-block: calc($space_half - 1px) $space_half;
line-height: $line_basic;
text-align: center;
}
#status {
width: 4em;
background-color: black;
color: white;
}
#spinner {
width: 2.5em;
background-color: $color_back_basic;
color: black;
span {
animation: rotate 1s linear infinite;
}
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#intro { #intro {
@extend %full_parent; @extend %full_parent;
@ -89,8 +157,8 @@ $z-indexes: (
.toast { .toast {
z-index: map.get($z-indexes, "toast"); z-index: map.get($z-indexes, "toast");
position: fixed; position: fixed;
right: $space_half; right: $space_half;
bottom: $space_double; bottom: $space_double;
p { p {
color: white; color: white;