0.1.0
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
// file: kb_app_demo/frontend/ts/splash.ts
|
||||
// version: 4
|
||||
|
||||
//! Splash-screen event handling for the Tauri startup window.
|
||||
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import type { SplashOrder } from "./bindings/kb_app_demo/splash/SplashOrder";
|
||||
import { frontendError, frontendDebug, installFrontendConsoleBridge } from "./frontend_log";
|
||||
|
||||
|
||||
const splashTarget = "kb_app_demo.frontend.splash";
|
||||
const defaultFadeDurationMs = 3000;
|
||||
let activeOpacityFrame: number | null = null;
|
||||
|
||||
function normalizeDurationMs(value: number | null | undefined): number {
|
||||
if (typeof value !== "number") {
|
||||
return defaultFadeDurationMs;
|
||||
}
|
||||
if (!Number.isFinite(value) || value <= 0) {
|
||||
return defaultFadeDurationMs;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function easeInOut(progress: number): number {
|
||||
if (progress < 0.5) {
|
||||
return 2 * progress * progress;
|
||||
}
|
||||
return 1 - Math.pow(-2 * progress + 2, 2) / 2;
|
||||
}
|
||||
|
||||
async function animateOpacity(element: HTMLElement, fromOpacity: number, toOpacity: number, durationMs: number): Promise<void> {
|
||||
frontendDebug(splashTarget, `Animating from ${fromOpacity} to ${toOpacity} over ${durationMs}ms`);
|
||||
if (activeOpacityFrame !== null) {
|
||||
cancelAnimationFrame(activeOpacityFrame);
|
||||
activeOpacityFrame = null;
|
||||
}
|
||||
element.style.opacity = fromOpacity.toString();
|
||||
element.style.willChange = "opacity";
|
||||
await new Promise<void>(resolve => requestAnimationFrame(() => resolve()));
|
||||
await new Promise<void>(resolve => {
|
||||
const startedAt = performance.now();
|
||||
const opacityDelta = toOpacity - fromOpacity;
|
||||
const updateOpacity = (currentTime: number): void => {
|
||||
const elapsedMs = currentTime - startedAt;
|
||||
const rawProgress = Math.min(elapsedMs / durationMs, 1);
|
||||
const easedProgress = easeInOut(rawProgress);
|
||||
const nextOpacity = fromOpacity + opacityDelta * easedProgress;
|
||||
element.style.opacity = nextOpacity.toString();
|
||||
if (rawProgress >= 1) {
|
||||
element.style.opacity = toOpacity.toString();
|
||||
element.style.willChange = "auto";
|
||||
activeOpacityFrame = null;
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
activeOpacityFrame = requestAnimationFrame(updateOpacity);
|
||||
};
|
||||
activeOpacityFrame = requestAnimationFrame(updateOpacity);
|
||||
});
|
||||
}
|
||||
|
||||
function addLogMessage(message: string): void {
|
||||
const debugInfo = document.getElementById("debug-info");
|
||||
if (!debugInfo) {
|
||||
return;
|
||||
}
|
||||
const time = new Date().toLocaleTimeString();
|
||||
debugInfo.innerHTML += `${time}: ${message}<br>`;
|
||||
frontendDebug(splashTarget, `addLogMessage: ${message}`);
|
||||
}
|
||||
|
||||
function addMessage(message: string, status: string): void {
|
||||
const messagesContainer = document.getElementById("messages-container");
|
||||
if (!messagesContainer) {
|
||||
return;
|
||||
}
|
||||
const messageElement = document.createElement("div");
|
||||
messageElement.className = `splash-message ${status}`;
|
||||
messageElement.textContent = message;
|
||||
messagesContainer.appendChild(messageElement);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
frontendDebug(splashTarget, `addMessage: ${message} - ${status}`);
|
||||
}
|
||||
|
||||
async function handleSplashOrder(order: SplashOrder): Promise<void> {
|
||||
const durationMs = normalizeDurationMs(order.duration_ms);
|
||||
const container = document.getElementById("splash-container");
|
||||
if (order.order === "fadein" && container) {
|
||||
frontendDebug(splashTarget, `fadein`);
|
||||
await animateOpacity(container, 0, 1, durationMs);
|
||||
return;
|
||||
}
|
||||
if (order.order === "fadeout" && container) {
|
||||
frontendDebug(splashTarget, `fadeout`);
|
||||
await animateOpacity(container, 1, 0, durationMs);
|
||||
return;
|
||||
}
|
||||
if (order.order === "add_msg" && order.msg && order.status) {
|
||||
addMessage(order.msg, order.status);
|
||||
return;
|
||||
}
|
||||
if (order.order === "add_log" && order.msg) {
|
||||
addLogMessage(order.msg);
|
||||
return;
|
||||
}
|
||||
frontendError(splashTarget, `unknown splash order: ${order.order}`);
|
||||
}
|
||||
|
||||
function initializeSplashDom(): void {
|
||||
const container = document.getElementById("splash-container");
|
||||
if (container) {
|
||||
container.style.opacity = "0";
|
||||
container.style.willChange = "opacity";
|
||||
}
|
||||
installFrontendConsoleBridge(splashTarget);
|
||||
}
|
||||
|
||||
async function initializeSplashListeners(): Promise<void> {
|
||||
await listen<SplashOrder>("splash", event => {
|
||||
void handleSplashOrder(event.payload);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
initializeSplashDom();
|
||||
void initializeSplashListeners();
|
||||
frontendDebug(splashTarget, "splash window loaded");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user