mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-12 21:02:14 +00:00
hero: one wash for both themes — revert the continuous-blend machinery
Reverts the per-frame color sampling and luminance-blended wash modes: the right fix was removing the second wash entirely. Cards now dim via color multiply in both themes (hue-true; the light theme's alpha wash toward the paper shifted footage pastel, and having two treatments is what made theme fades show a mode switch at all). watchThemeColors returns to the simple flip + settle double-derive. Claude-Session: https://claude.ai/code/session_01YBoaBEt3gynzTwogBFtn4B
This commit is contained in:
1 parent
0083dc140f
commit
7bc1407648
3 files changed
+18
-33
No files matched your search
@@ -453,7 +453,7 @@ export const galaxyLookTuner = defineTunables("galaxyLook", "Galaxy look", {
|
||||
max: 1,
|
||||
step: 0.02,
|
||||
default: 1,
|
||||
info: "Overall footage strength (dark theme dims via color, light theme washes via opacity).",
|
||||
info: "Overall footage strength — dims via color multiply, the same in both themes.",
|
||||
},
|
||||
aberration: {
|
||||
label: "Dispersion",
|
||||
|
||||
@@ -335,9 +335,9 @@ function GalaxyScene({
|
||||
boostCard: -1,
|
||||
});
|
||||
|
||||
// NOTE: no boolean dark flag — the light/dark wash treatment blends
|
||||
// continuously by the LERPED background's luminance in the frame loop,
|
||||
// so a theme fade never snaps between wash modes.
|
||||
// One wash for both themes: cards dim via color multiply (hue-true —
|
||||
// an alpha wash toward the paper shifted footage pastel in light mode,
|
||||
// and two treatments meant a visible mode switch during theme fades).
|
||||
|
||||
// One <video>, one shared texture, and one cover-fit window per clip.
|
||||
const videos = useMemo(
|
||||
@@ -807,10 +807,6 @@ function GalaxyScene({
|
||||
cs.line.lerp(cs.tLine, themeK);
|
||||
lineMat.color.copy(cs.line);
|
||||
tickMat.color.copy(cs.line);
|
||||
// Wash-mode blend: light theme washes cards via opacity, dark theme
|
||||
// dims via color. Driven continuously by the LERPED background's
|
||||
// luminance — a binary flag snapped the treatment mid-fade.
|
||||
const darkK = smoothstep(1.95, 1.05, cs.bg.x + cs.bg.y + cs.bg.z);
|
||||
|
||||
// Perf governor: EMA of the real frame time. Sustained drops below the
|
||||
// FPS floor shed cards (and their decode pressure) quickly; recovery
|
||||
@@ -1241,12 +1237,8 @@ function GalaxyScene({
|
||||
u.uClickW.value = pt.clickWidth * Math.max(W, H);
|
||||
u.uRadius.value = Math.min(lk.cornerPx, H * 0.49);
|
||||
u.uFrameA.value = lk.frameAlpha;
|
||||
const dimDark = lk.dim * solid + (1 - lk.dim * solid) * ck;
|
||||
const wash = solid * Math.sqrt(lk.dim);
|
||||
const alphaLight = lifecycle * waveK * (wash + (1 - wash) * ck);
|
||||
const alphaDark = lifecycle * waveK;
|
||||
u.uDim.value = 1 + (dimDark - 1) * darkK;
|
||||
u.uAlpha.value = alphaLight + (alphaDark - alphaLight) * darkK;
|
||||
u.uDim.value = lk.dim * solid + (1 - lk.dim * solid) * ck;
|
||||
u.uAlpha.value = lifecycle * waveK;
|
||||
|
||||
// A targeted card's clip outranks everything in the decode budget so
|
||||
// it plays immediately, wherever it is on the journey.
|
||||
|
||||
@@ -67,35 +67,28 @@ export function watchThemeColors(
|
||||
onChange: (colors: ThemeColors) => void,
|
||||
): () => void {
|
||||
const derive = () => onChange(deriveThemeColors());
|
||||
// Theme flips animate the tokens over 0.4s (see globals.css). Sampling
|
||||
// just the endpoints left WebGL consumers chasing a stale target until
|
||||
// the fade ended (a visible late shift); instead, sample the computed
|
||||
// colors EVERY FRAME through the transition window so canvas layers
|
||||
// track the DOM's animated tokens exactly.
|
||||
let raf = 0;
|
||||
let until = 0;
|
||||
const sampleLoop = () => {
|
||||
// Theme flips animate the tokens over 0.4s (see globals.css), so a
|
||||
// derive at flip time reads MID-transition values. Deliver both: the
|
||||
// immediate read (consumers that lerp start moving at once) and a final
|
||||
// read after the fade settles so targets land on the true colors.
|
||||
let settleTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
const deriveTwice = () => {
|
||||
derive();
|
||||
if (performance.now() < until) raf = requestAnimationFrame(sampleLoop);
|
||||
};
|
||||
const onFlip = () => {
|
||||
until = performance.now() + 550;
|
||||
cancelAnimationFrame(raf);
|
||||
derive();
|
||||
raf = requestAnimationFrame(sampleLoop);
|
||||
clearTimeout(settleTimer);
|
||||
settleTimer = setTimeout(derive, 500);
|
||||
};
|
||||
derive();
|
||||
|
||||
const observer = new MutationObserver(onFlip);
|
||||
const observer = new MutationObserver(deriveTwice);
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
mq.addEventListener("change", onFlip);
|
||||
mq.addEventListener("change", deriveTwice);
|
||||
return () => {
|
||||
cancelAnimationFrame(raf);
|
||||
clearTimeout(settleTimer);
|
||||
observer.disconnect();
|
||||
mq.removeEventListener("change", onFlip);
|
||||
mq.removeEventListener("change", deriveTwice);
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user