From 0b429321cccf1f11e9fd62da5829cbde6b034843 Mon Sep 17 00:00:00 2001 From: xixn2 Date: Sun, 26 Jul 2026 12:08:50 +0900 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20(fix)=20#23=20::=20=EB=8F=8B?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=EB=A0=8C=EC=A6=88=EB=A5=BC=20=EC=B9=B4?= =?UTF-8?q?=EB=93=9C=EB=B3=84=20=EB=93=B1=EC=9E=A5=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=EB=A1=9C=20=ED=8C=90=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/sections/CapabilitiesScene.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/components/sections/CapabilitiesScene.tsx b/components/sections/CapabilitiesScene.tsx index 72efe66..506bd2e 100644 --- a/components/sections/CapabilitiesScene.tsx +++ b/components/sections/CapabilitiesScene.tsx @@ -19,15 +19,17 @@ const ACCENTS = [ // at ~5 wheel notches each, plus the sticky 100vh. const SCENE_LENGTH_VH = 440; const TILT = 10; -/** Scroll progress at which the magnifier lens becomes interactive — the point - * where the label, headline and sub copy have all landed (the cards are still - * rising in below, and the lens only shows over rows anyway). */ -const LENS_ARM_AT = 0.48; +/** How far a single card must have dealt in (0→1) before the lens works on it. */ +const LENS_ARM_AT = 0.98; /** Vertical breathing room kept between the scene and the viewport edges. */ const SCENE_PAD = 72; const MONO = "'JetBrains Mono', ui-monospace, monospace"; const clamp = (v: number, a: number, b: number) => Math.min(b, Math.max(a, v)); +/** Entrance progress of card `c` at scene progress `p` — mirrors the transform + * timeline below so the lens gate and the animation never disagree. */ +const cardProgress = (c: number, p: number) => + 1 - Math.pow(1 - clamp((p - 0.4 - c * 0.07) / 0.22, 0, 1), 3); const ease = (t: number) => 1 - Math.pow(1 - t, 3); const fr = (n: number) => n - Math.floor(n); @@ -227,15 +229,19 @@ export default function CapabilitiesScene() { let over = -1; // The lens only arms once the scene has finished dealing itself out — // popping a magnifier over cards that are still flying in reads as a bug. - const armed = p >= LENS_ARM_AT; // Hit-testing walks up to 30 elements with getBoundingClientRect, which // forces layout each time. The answer can only change when the cursor // moves, so reuse the previous result on idle frames. - const needsHitTest = armed && mouseMoved; + const needsHitTest = mouseMoved; mouseMoved = false; for (let c = 0; c < 4 && over < 0 && needsHitTest; c++) { const d = capData[c]; if (!d) continue; + // Per-card gate: each card deals in on its own schedule, so a single + // scene-wide threshold would arm the lens over cards that are still + // flying in (and the lens content is a static snapshot, so it would + // sit offset from the real card). Only cards that have landed qualify. + if (cardProgress(c, p) < LENS_ARM_AT) continue; const targets = d.rows.slice(); if (d.head) targets.unshift(d.head); if (d.chipsRow) targets.push(d.chipsRow);