diff --git a/landing/app/components/sections/hero-dataflow.tsx b/landing/app/components/sections/hero-dataflow.tsx index 845e5c4..b7084f2 100644 --- a/landing/app/components/sections/hero-dataflow.tsx +++ b/landing/app/components/sections/hero-dataflow.tsx @@ -36,7 +36,7 @@ export function HeroDataFlow() { className={`absolute -translate-x-1/2 -translate-y-1/2 whitespace-nowrap text-[13px] font-semibold text-on-stage-soft/80 transition-opacity duration-500 ${ phase === "cluster" ? "opacity-100" : "opacity-0" }`} - style={{ left: `${anchor.x * 100}%`, top: `${anchor.labelY * 100}%` }} + style={{ left: `${anchor.screenX * 100}%`, top: `${anchor.labelY * 100}%` }} > {anchor.label} @@ -44,7 +44,7 @@ export function HeroDataFlow() { {/* 카피는 상단 55%, 데이터 흐름은 하단 밴드. 둘이 겹치지 않게 층을 나눈다. */} -
+
{/* 히어로 카피에는 진입 애니메이션을 걸지 않는다. 여기가 LCP 요소라 opacity:0 으로 시작하면 하이드레이션 전까지 첫 화면이 비고, 광고 유입처럼 JS 가 느린 환경에서 그대로 손실이 된다. 모션은 배경 캔버스가 맡는다. */} diff --git a/landing/app/components/ui/data-flow-canvas.tsx b/landing/app/components/ui/data-flow-canvas.tsx index 3dbdeda..843bd0b 100644 --- a/landing/app/components/ui/data-flow-canvas.tsx +++ b/landing/app/components/ui/data-flow-canvas.tsx @@ -70,13 +70,25 @@ export const perspective = (z: number) => 0.42 + 0.58 * z /* 분류 단계에서 각 무리가 앉는 자리와 깊이. 무리마다 z 평면을 하나씩 준다 — 입자마다 z 를 따로 주면 격자가 3D 로 뒤틀려 - 표로 안 읽힌다. 무리 단위로 주면 격자는 온전한 채 그룹 사이에만 깊이가 생긴다. */ -export const CLUSTER_ANCHORS = [ - { key: "item", x: 0.17, z: 0.9, labelY: 0.525, label: "거래 품목" }, - { key: "user", x: 0.45, z: 0.66, labelY: 0.525, label: "사용자" }, - { key: "partner", x: 0.76, z: 0.44, labelY: 0.525, label: "협력사" }, + 표로 안 읽힌다. 무리 단위로 주면 격자는 온전한 채 그룹 사이에만 깊이가 생긴다. + + x 는 "투영된 뒤 어디에 놓일지"를 먼저 정하고 역산한 값이다. + 투영은 깊이마다 다른 배율로 중앙으로 당기기 때문에(k = 0.42+0.58z), 세계 좌표를 + 눈대중으로 고르면 가장 깊은 무리가 제일 많이 끌려와 구도가 한쪽으로 쏠린다. */ +const CLUSTER_PLAN = [ + { key: "item", screenX: 0.19, z: 0.9, label: "거래 품목" }, + { key: "user", screenX: 0.5, z: 0.66, label: "사용자" }, + { key: "partner", screenX: 0.81, z: 0.44, label: "협력사" }, ] as const +export const CLUSTER_ANCHORS = CLUSTER_PLAN.map((c) => ({ + ...c, + /** 투영 전 세계 좌표 — 투영을 거치면 screenX 에 놓인다 */ + x: VANISH_X + (c.screenX - VANISH_X) / perspective(c.z), + /** 라벨은 격자 위. 투영 후 y 라서 화면 좌표 그대로 쓴다 */ + labelY: VANISH_Y + (0.5 - VANISH_Y) * perspective(c.z), +})) + /* 격자 배치 — 종류별 [열, 열간격(x, 화면비), 행간격(y, 밴드지역비)]. 간격은 글리프보다 확실히 커야 한다. 붙으면 표가 아니라 노이즈로 읽힌다. */ const GRID = [