я косяк, поправил веб. Капитан, работайте!
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* Блик по буквам логотипа в navbar (тёмная тема) — тот же drawShimmer, что в wesp-logo-loader.
|
||||
*/
|
||||
(function () {
|
||||
const LOGO_SRC = "/static/logo2.png";
|
||||
const CYCLE_MS = 3000;
|
||||
const SHIMMER_MS = 1000;
|
||||
const START_DELAY_MS = 350;
|
||||
|
||||
const mounts = new WeakMap();
|
||||
|
||||
function isDarkTheme() {
|
||||
return document.documentElement.getAttribute("data-theme") === "dark";
|
||||
}
|
||||
|
||||
function prefersReducedMotion() {
|
||||
return globalThis.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ?? false;
|
||||
}
|
||||
|
||||
function loadImage(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = () => reject(new Error(`Failed to load: ${src}`));
|
||||
img.src = src;
|
||||
});
|
||||
}
|
||||
|
||||
function disposeMount(link) {
|
||||
const state = mounts.get(link);
|
||||
if (!state) return;
|
||||
state.disposed = true;
|
||||
if (state.rafId) cancelAnimationFrame(state.rafId);
|
||||
state.canvas?.remove();
|
||||
link.classList.remove("nav-logo-link--canvas-shimmer");
|
||||
mounts.delete(link);
|
||||
}
|
||||
|
||||
async function mountCanvasShimmer(link) {
|
||||
if (!isDarkTheme() || prefersReducedMotion()) return;
|
||||
disposeMount(link);
|
||||
|
||||
let logoModule;
|
||||
try {
|
||||
logoModule = await import("/static/js/wesp-logo-loader.js?v=2");
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
const { createNavLogoShimmerBuffer, drawShimmer } = logoModule;
|
||||
let img;
|
||||
try {
|
||||
img = await loadImage(LOGO_SRC);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.className = "nav-logo__shimmer-canvas";
|
||||
canvas.setAttribute("aria-hidden", "true");
|
||||
link.classList.add("nav-logo-link--canvas-shimmer");
|
||||
link.appendChild(canvas);
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
const state = {
|
||||
disposed: false,
|
||||
rafId: 0,
|
||||
canvas,
|
||||
logoCanvas: null,
|
||||
layout: null,
|
||||
startAt: performance.now() + START_DELAY_MS,
|
||||
lastW: 0,
|
||||
lastH: 0,
|
||||
};
|
||||
mounts.set(link, state);
|
||||
|
||||
function rebuildBuffer() {
|
||||
const w = Math.max(1, Math.round(link.clientWidth));
|
||||
const h = Math.max(1, Math.round(link.clientHeight));
|
||||
if (w === state.lastW && h === state.lastH && state.logoCanvas) return;
|
||||
|
||||
state.lastW = w;
|
||||
state.lastH = h;
|
||||
const dpr = Math.min(globalThis.devicePixelRatio || 1, 2);
|
||||
canvas.width = Math.round(w * dpr);
|
||||
canvas.height = Math.round(h * dpr);
|
||||
canvas.style.width = `${w}px`;
|
||||
canvas.style.height = `${h}px`;
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
const built = createNavLogoShimmerBuffer(img, w, h, [255, 255, 255]);
|
||||
state.logoCanvas = built.logoCanvas;
|
||||
state.layout = built.layout;
|
||||
}
|
||||
|
||||
function tick(now) {
|
||||
if (state.disposed) return;
|
||||
if (!isDarkTheme()) {
|
||||
disposeMount(link);
|
||||
return;
|
||||
}
|
||||
|
||||
rebuildBuffer();
|
||||
|
||||
const w = state.lastW;
|
||||
const h = state.lastH;
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
|
||||
const elapsed = now - state.startAt;
|
||||
if (elapsed >= 0 && state.logoCanvas && state.layout) {
|
||||
const cycle = elapsed % CYCLE_MS;
|
||||
if (cycle < SHIMMER_MS) {
|
||||
const shimmerT = cycle / SHIMMER_MS;
|
||||
ctx.drawImage(state.logoCanvas, 0, 0, w, h);
|
||||
drawShimmer(ctx, state.layout, state.logoCanvas, shimmerT, false);
|
||||
}
|
||||
}
|
||||
|
||||
state.rafId = requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
rebuildBuffer();
|
||||
state.rafId = requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (prefersReducedMotion()) return;
|
||||
|
||||
document
|
||||
.querySelectorAll(".mobile-navbar .nav-logo-link, .mobile-brand .nav-logo-link")
|
||||
.forEach((link) => {
|
||||
if (isDarkTheme()) {
|
||||
mountCanvasShimmer(link);
|
||||
} else {
|
||||
disposeMount(link);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onThemeChange() {
|
||||
init();
|
||||
}
|
||||
|
||||
window.WespNavLogoShimmer = { init, disposeMount };
|
||||
|
||||
const themeObserver = new MutationObserver(onThemeChange);
|
||||
themeObserver.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
|
||||
/* init вызывается из wesp-zootech-nav.js после вставки navbar и из wesp-page-enter после enter-done */
|
||||
})();
|
||||
Reference in New Issue
Block a user