mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 17:21:05 +00:00
feat: add performant webgl loading screens
This commit is contained in:
+224
-88
@@ -1,102 +1,238 @@
|
||||
import browser from "webextension-polyfill";
|
||||
import stringToHTML from "@/seqta/utils/stringToHTML";
|
||||
import loadingSpinner from "./loading-spinner.html?raw";
|
||||
import loadingOverlay from "./loading-overlay.html?raw";
|
||||
import { startLoadingCanvas } from "./loadingCanvas";
|
||||
import {
|
||||
pickLoadingVariant,
|
||||
type LoadingVariant,
|
||||
} from "./loadingVariants";
|
||||
|
||||
export function AppendLoadingSymbol(givenID: any, position: any) {
|
||||
let loadingsymbol = stringToHTML(/* html */ `
|
||||
<div id="${givenID}">
|
||||
let stopCanvas: (() => void) | null = null;
|
||||
|
||||
const loadingStyles = /* css */ `
|
||||
.bkloading {
|
||||
--bk-line-color: rgba(255, 255, 255, 0.08);
|
||||
--bk-line-accent: rgba(255, 255, 255, 0.15);
|
||||
--bk-line-blue: rgba(96, 165, 250, 0.42);
|
||||
--bk-grid-color: rgba(255, 255, 255, 0.028);
|
||||
--bk-spin-outer: 1s;
|
||||
--bk-spin-inner: 3s;
|
||||
--bk-spin-small: 3s;
|
||||
--bk-stage-name: bkloading-stage-in;
|
||||
--bk-stage-duration: 0.85s;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1000000;
|
||||
overflow: hidden;
|
||||
contain: strict;
|
||||
color: #f4f4f5;
|
||||
background:
|
||||
radial-gradient(ellipse 70% 45% at 50% 95%, rgba(37, 99, 235, 0.12), transparent 62%),
|
||||
linear-gradient(180deg, #010101 0%, #040404 50%, #080808 100%);
|
||||
opacity: 1;
|
||||
transition: opacity 0.85s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.bkloading.closeLoading {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bkloading__canvas {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bkloading__vignette {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(ellipse at center, transparent 32%, rgba(0, 0, 0, 0.75) 100%);
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.bkloading__content {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bkloading__logo-stage,
|
||||
.bkloading-inline {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bkloading__logo-stage {
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
animation-name: var(--bk-stage-name);
|
||||
animation-duration: var(--bk-stage-duration);
|
||||
animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.bkloading-inline {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.bkloading__logo-stage .svg,
|
||||
.bkloading-inline .svg {
|
||||
transform-origin: center center;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
backface-visibility: hidden;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.bkloading__logo-stage .logo,
|
||||
.bkloading-inline .logo {
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
|
||||
.bkloading__logo-stage .logo {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.bkloading__logo-stage .big-circle {
|
||||
margin: -90px;
|
||||
animation: bkloading-spin var(--bk-spin-inner) linear infinite;
|
||||
}
|
||||
|
||||
.bkloading__logo-stage .small-circle {
|
||||
margin: -67px;
|
||||
animation: bkloading-spin var(--bk-spin-small) linear infinite;
|
||||
}
|
||||
|
||||
.bkloading__logo-stage .outer-circle {
|
||||
margin: -110px;
|
||||
animation: bkloading-spin-reverse var(--bk-spin-outer) linear infinite;
|
||||
}
|
||||
|
||||
.bkloading-inline .big-circle {
|
||||
margin: -88px;
|
||||
animation: bkloading-spin 3s linear infinite;
|
||||
}
|
||||
|
||||
.bkloading-inline .small-circle {
|
||||
margin: -66px;
|
||||
animation: bkloading-spin-reverse 3s linear infinite;
|
||||
}
|
||||
|
||||
.bkloading-inline .outer-circle {
|
||||
margin: -108px;
|
||||
animation: bkloading-spin 4.5s linear infinite;
|
||||
}
|
||||
|
||||
.bkloading__version {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
bottom: 10px;
|
||||
font-size: 0.95rem;
|
||||
letter-spacing: 0.02em;
|
||||
color: rgba(255, 255, 255, 0.35);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes bkloading-stage-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, 12px, 0) scale(0.94);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bkloading-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bkloading-spin-reverse {
|
||||
to {
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.bkloading__logo-stage .big-circle,
|
||||
.bkloading__logo-stage .small-circle,
|
||||
.bkloading__logo-stage .outer-circle,
|
||||
.bkloading-inline .big-circle,
|
||||
.bkloading-inline .small-circle,
|
||||
.bkloading-inline .outer-circle {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function overlayWithSpinner(): string {
|
||||
return loadingOverlay.replace(
|
||||
"<div class=\"bkloading__logo-stage\"></div>",
|
||||
`<div class="bkloading__logo-stage">${loadingSpinner}</div>`,
|
||||
);
|
||||
}
|
||||
|
||||
function applyVariantTheme(root: HTMLElement, variant: LoadingVariant) {
|
||||
const { theme } = variant;
|
||||
root.dataset.variant = variant.id;
|
||||
root.style.background = theme.background;
|
||||
root.style.setProperty("--bk-spin-outer", theme.spinOuter);
|
||||
root.style.setProperty("--bk-spin-inner", theme.spinInner);
|
||||
root.style.setProperty("--bk-spin-small", theme.spinSmall);
|
||||
root.style.setProperty("--bk-stage-name", theme.stageName);
|
||||
root.style.setProperty("--bk-stage-duration", theme.stageDuration);
|
||||
|
||||
const vignette = root.querySelector(".bkloading__vignette") as HTMLElement | null;
|
||||
if (vignette) vignette.style.opacity = String(theme.vignetteOpacity);
|
||||
}
|
||||
|
||||
function startCanvasForRoot(root: HTMLElement, variant: LoadingVariant) {
|
||||
stopCanvas?.();
|
||||
const canvas = root.querySelector(".bkloading__canvas") as HTMLCanvasElement | null;
|
||||
stopCanvas = canvas ? startLoadingCanvas(canvas, root, variant) : null;
|
||||
}
|
||||
|
||||
export function AppendLoadingSymbol(givenID: string, position: string) {
|
||||
const loadingsymbol = stringToHTML(/* html */ `
|
||||
<div id="${givenID}" class="bkloading-inline">
|
||||
${loadingSpinner}
|
||||
</div>`).firstChild;
|
||||
|
||||
document.querySelector(position).appendChild(loadingsymbol);
|
||||
document.querySelector(position)?.appendChild(loadingsymbol!);
|
||||
}
|
||||
|
||||
export function stopLoadingAnimation() {
|
||||
stopCanvas?.();
|
||||
stopCanvas = null;
|
||||
}
|
||||
|
||||
export default function loading() {
|
||||
let loadinghtml = stringToHTML(/* html */ `
|
||||
<div class="bkloading" id="loading">
|
||||
<style>
|
||||
.bkloading {
|
||||
transition: color 1ms linear, opacity 1s ease-in-out;
|
||||
background-color: rgb(229, 231, 235);
|
||||
color: black;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
transition: 1s;
|
||||
height: 100%;
|
||||
z-index: 1000000;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
stopLoadingAnimation();
|
||||
|
||||
.closeLoading {
|
||||
opacity: 0;
|
||||
}
|
||||
const variant = pickLoadingVariant();
|
||||
|
||||
.dark .bkloading {
|
||||
background-color: rgb(26, 26, 26);
|
||||
color: white;
|
||||
}
|
||||
const loadinghtml = stringToHTML(/* html */ `
|
||||
<div class="bkloading" id="loading" data-variant="${variant.id}">
|
||||
<style>${loadingStyles}</style>
|
||||
${overlayWithSpinner()}
|
||||
<div class="bkloading__version">v${browser.runtime.getManifest().version}</div>
|
||||
</div>`);
|
||||
|
||||
.svg {
|
||||
transform-origin: center;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
will-change: transform;
|
||||
}
|
||||
.logo {
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.big-circle {
|
||||
margin: -88px;
|
||||
will-change: transform;
|
||||
animation-timing-function: ease;
|
||||
animation: spin 3s linear infinite;
|
||||
-moz-animation: spin 3s linear infinite;
|
||||
}
|
||||
.small-circle {
|
||||
margin: -66px;
|
||||
will-change: transform;
|
||||
animation-timing-function: ease;
|
||||
animation: spin 3s linear infinite;
|
||||
-moz-animation: spin 3s linear infinite;
|
||||
}
|
||||
.outer-circle {
|
||||
margin: -108px;
|
||||
will-change: transform;
|
||||
animation-direction: alternate-reverse;
|
||||
animation: spinback 1s linear infinite;
|
||||
-moz-animation: spinback 1s linear infinite;
|
||||
}
|
||||
@-moz-keyframes spin {
|
||||
100% {
|
||||
-moz-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes spin {
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes spinback {
|
||||
100% {
|
||||
-webkit-transform: rotate(-360deg);
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
${loadingSpinner}
|
||||
<div style="position: absolute;bottom: 0;right: 0;padding: 10px;color: #4f4f4f;text-anchor: middle;font-size: 20px;">v${
|
||||
browser.runtime.getManifest().version
|
||||
}</div></div>`);
|
||||
var html = document.getElementsByTagName("html")[0];
|
||||
html.append(loadinghtml.firstChild!);
|
||||
const root = loadinghtml.firstChild as HTMLElement;
|
||||
document.documentElement.append(root);
|
||||
|
||||
applyVariantTheme(root, variant);
|
||||
startCanvasForRoot(root, variant);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user