diff --git a/package-lock.json b/package-lock.json index 118b07f..ebcdd29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "univerlab-web", - "version": "1.19.5", + "version": "1.19.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "univerlab-web", - "version": "1.19.5", + "version": "1.19.6", "license": "ISC", "dependencies": { "@astrojs/sitemap": "^3.7.3" diff --git a/package.json b/package.json index b9dab45..b12ba71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "univerlab-web", - "version": "1.19.5", + "version": "1.19.6", "description": "

UniverLab — landing

", "scripts": { "dev": "astro dev", diff --git a/src/__tests__/home-lantern.test.ts b/src/__tests__/home-lantern.test.ts index 8d9b636..afa9970 100644 --- a/src/__tests__/home-lantern.test.ts +++ b/src/__tests__/home-lantern.test.ts @@ -287,15 +287,14 @@ describe('Home — tuning into an experiment', () => { expect(GLOBAL).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*animation: none !important/); }); - it('shows static on the screen while it tunes', () => { - expect(TUNE).toContain("classList.add('is-tuning')"); - expect(LANTERN).toMatch(/screen\.classList\.contains\('is-tuning'\)/); - expect(LANTERN).toContain('drawSnow(now)'); + it('navigates at once, with no static on the screen', () => { + expect(TUNE).not.toContain('is-tuning'); + expect(TUNE).not.toContain('setTimeout'); + expect(LANTERN).not.toMatch(/snow|is-tuning/i); }); - it('clears the static and the shared name on Back restore', () => { + it('clears the shared name on Back restore', () => { expect(HOME).toContain("addEventListener('pageshow', untune)"); - expect(TUNE).toContain("classList.remove('is-tuning')"); expect(TUNE).toContain("removeProperty('view-transition-name')"); expect(LAYOUT).not.toContain('crt-restore'); }); diff --git a/src/scripts/lantern.ts b/src/scripts/lantern.ts index b6591ea..4819c06 100644 --- a/src/scripts/lantern.ts +++ b/src/scripts/lantern.ts @@ -406,23 +406,6 @@ export function startLantern(): void { g.putImageData(data, 0, 0); return tile; })(); - // The static a tuning screen shows before its experiment opens (tune.ts): - // the same tile size, opaque greys instead of a faint grain. - const snow = (() => { - const tile = document.createElement('canvas'); - tile.width = tile.height = 128; - const g = tile.getContext('2d'); - if (!g) return tile; - const data = g.createImageData(128, 128); - for (let i = 0; i < data.data.length; i += 4) { - const v = Math.random() * 255; - data.data[i] = data.data[i + 1] = data.data[i + 2] = v; - data.data[i + 3] = 255; - } - g.putImageData(data, 0, 0); - return tile; - })(); - // Cost model (measured with a 4x-throttled CPU): every fill on this canvas // is raster work on the main thread — redrawn at full size each frame, and // resized each frame, the film alone took a third of it. So it runs at half @@ -434,7 +417,6 @@ export function startLantern(): void { let fw = 0; let fh = 0; let pattern: CanvasPattern | null = null; - let snowPattern: CanvasPattern | null = null; let vignette: CanvasGradient | null = null; let lastFilm = 0; let onView = true; @@ -446,7 +428,6 @@ export function startLantern(): void { film.width = fw; film.height = fh; pattern = ctx.createPattern(grain, 'repeat'); - snowPattern = ctx.createPattern(snow, 'repeat'); vignette = ctx.createRadialGradient( fw / 2, fh / 2, @@ -464,33 +445,8 @@ export function startLantern(): void { onView = entry?.isIntersecting ?? true; }).observe(screen); - /** Tuning: dense static that swells in, with a bright band rolling down. */ - let tuneAt = 0; - const drawSnow = (now: number): void => { - if (!ctx) return; - tuneAt ||= now; - const k = Math.min(1, (now - tuneAt) / 90); - ctx.save(); - ctx.translate(-Math.random() * 128, -Math.random() * 128); - ctx.globalAlpha = 0.25 + 0.6 * k; - ctx.fillStyle = snowPattern ?? '#888'; - ctx.fillRect(0, 0, fw + 128, fh + 128); - ctx.restore(); - const band = fh * 0.22; - const y = (((now - tuneAt) / 170) * (fh + band)) % (fh + band) - band; - ctx.globalAlpha = 0.18; - ctx.fillStyle = '#fff'; - ctx.fillRect(0, y, fw, band); - ctx.globalAlpha = 1; - }; - const drawFilm = (now: number): void => { if (!ctx || !onView || fw === 0 || fh === 0) return; - if (screen.classList.contains('is-tuning')) { - drawSnow(now); - return; - } - tuneAt = 0; if (now - lastFilm < FILM_MS) return; lastFilm = now; const w = fw; diff --git a/src/scripts/tune.ts b/src/scripts/tune.ts index f461f90..2fb57ce 100644 --- a/src/scripts/tune.ts +++ b/src/scripts/tune.ts @@ -1,13 +1,11 @@ /** Home → experiment: the lantern's screen tunes in to the experiment. * - * A short burst of static (lantern.ts paints it while `#screen.is-tuning`), - * then the browser navigates and the cross-document view transition zooms + * The browser navigates at once and the cross-document view transition zooms * the source into the destination's hero: both carry `exp-` in the * `exp-zoom` class (ExperimentLayout's hero, global.css's timing). Engines - * without cross-document view transitions keep the static and load plainly. - * Under reduced motion it navigates at once. + * without cross-document view transitions load plainly. Under reduced motion + * it navigates without naming the source. */ -const STATIC_MS = 170; export function tuneNavigate(href: string, exp: string, source: HTMLElement): void { if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { @@ -16,14 +14,12 @@ export function tuneNavigate(href: string, exp: string, source: HTMLElement): vo } source.style.setProperty('view-transition-name', `exp-${exp}`); source.style.setProperty('view-transition-class', 'exp-zoom'); - document.getElementById('screen')?.classList.add('is-tuning'); - window.setTimeout(() => window.location.assign(href), STATIC_MS); + window.location.assign(href); } -/** Back restores the bfcache snapshot mid-tune: drop the static and the - * shared name, or the next click would carry two `exp-*` names. */ +/** Back restores the bfcache snapshot mid-tune: drop the shared name, or the + * next click would carry two `exp-*` names. */ export function untune(): void { - document.getElementById('screen')?.classList.remove('is-tuning'); document.querySelectorAll('#screen, .plate[data-exp]').forEach((el) => { el.style.removeProperty('view-transition-name'); el.style.removeProperty('view-transition-class'); diff --git a/src/views/Home.astro b/src/views/Home.astro index 2350164..4067e97 100644 --- a/src/views/Home.astro +++ b/src/views/Home.astro @@ -582,7 +582,7 @@ const skyLines = Array.from({ length: 5 }, (_, i) => ({ import { shouldScreenNavigate } from '../lib/lantern-navigate'; // A machine opens its experiment page by tuning the screen in (tune.ts): - // static, then the screen zooms into the page's hero. Pointing at a machine + // the screen zooms into the page's hero. Pointing at a machine // already projects it, so the screen is the source while it is on view; a // machine clicked with the screen scrolled away zooms from itself. const screen = document.getElementById('screen');