// Variant 3 — Wave Reveal
// Brand wave가 화면을 가로지르며 3개 영역(Web → Security → Marketing)을
// 차례로 reveal. 마지막에 합쳐지며 로고 등장. 추상 모션 중심.
function ServiceTile({ label, korLabel, desc, icon, color, accent, children, x, y, scale = 1, opacity = 1 }) {
return (
);
}
function FeatureChips({ items, color }) {
return (
{items.map((it, i) => (
{it}
))}
);
}
// Big wave layer — fills the canvas with a flowing wave that morphs.
function BigWave({ time, intensity = 1, baseColor = WP.blue600, accentColor = WP.cyan500, yCenter = 540, opacity = 1, phaseOffset = 0 }) {
const W = 1920;
const H = 1080;
const buildPath = (offsetY, freq, amp, phase) => {
const N = 40;
const pts = [];
for (let i = 0; i <= N; i++) {
const x = (i / N) * W;
const y = yCenter + offsetY + Math.sin((i / N) * freq * Math.PI * 2 + phase) * amp * intensity;
pts.push([x, y]);
}
let d = `M ${pts[0][0]} ${pts[0][1]}`;
for (let i = 1; i < pts.length; i++) {
const cpx = (pts[i - 1][0] + pts[i][0]) / 2;
d += ` Q ${pts[i - 1][0]} ${pts[i - 1][1]}, ${cpx} ${(pts[i - 1][1] + pts[i][1]) / 2}`;
}
d += ` L ${W} ${H} L 0 ${H} Z`;
return d;
};
return (
);
}
function Variant3() {
const T = useTime();
// Timing — sequential reveal of 3 services then unite
// 0–1 intro (wave entering, dark canvas)
// 1–4 Web reveal
// 4–7 Security reveal (wave sweeps from right)
// 7–10 Marketing reveal
// 10–13 wave swells, all 3 collapse into logo
// 13–15 logo + slogan hold
// helper: per-service window with cross-fade
const window_ = (start, end) => {
const fadeIn = clamp((T - start) / 0.7, 0, 1);
const fadeOut = 1 - clamp((T - end) / 0.7, 0, 1);
return Math.min(fadeIn, fadeOut);
};
const webOp = window_(1.2, 4.2);
const secOp = window_(4.0, 7.2);
const mktOp = window_(7.0, 10.0);
const webEntry = clamp((T - 1.0) / 0.8, 0, 1);
const secEntry = clamp((T - 3.8) / 0.8, 0, 1);
const mktEntry = clamp((T - 6.8) / 0.8, 0, 1);
// Logo phase
const logoStart = 10.5;
const logoP = clamp((T - logoStart) / 1.5, 0, 1);
const logoE = Easing.easeOutCubic(logoP);
const sloganP = clamp((T - 11.5) / 1.5, 0, 1);
const sloganE = Easing.easeOutCubic(sloganP);
// Wave intensity — swells at unity moment
const swellP = clamp((T - 9.5) / 2.0, 0, 1);
const swell = Math.sin(swellP * Math.PI) * 0.6 + 1;
// wave colors transition between scenes
const sceneIdx = T < 4 ? 0 : T < 7 ? 1 : T < 10 ? 2 : 3;
const sceneColors = [
{ base: WP.blue600, accent: WP.cyan500 }, // web
{ base: WP.navy600, accent: WP.blue400 }, // security
{ base: WP.blue700, accent: WP.orange500 }, // marketing
{ base: WP.blue600, accent: WP.cyan500 }, // unity
];
// smooth interpolation between scene colors based on T windows
const colorAt = (t) => {
if (t < 4) return sceneColors[0];
if (t < 4.5) {
const k = (t - 4) / 0.5;
return {
base: mixColor(sceneColors[0].base, sceneColors[1].base, k),
accent: mixColor(sceneColors[0].accent, sceneColors[1].accent, k),
};
}
if (t < 7) return sceneColors[1];
if (t < 7.5) {
const k = (t - 7) / 0.5;
return {
base: mixColor(sceneColors[1].base, sceneColors[2].base, k),
accent: mixColor(sceneColors[1].accent, sceneColors[2].accent, k),
};
}
if (t < 10) return sceneColors[2];
if (t < 10.5) {
const k = (t - 10) / 0.5;
return {
base: mixColor(sceneColors[2].base, sceneColors[3].base, k),
accent: mixColor(sceneColors[2].accent, sceneColors[3].accent, k),
};
}
return sceneColors[3];
};
const colors = colorAt(T);
return (
{/* Big back wave (slow) */}
{/* Front wave */}
{/* Floating wave streaks */}
{/* Section index ribbon */}
{['WEB', 'SECURITY', 'MARKETING'].map((l, i) => {
const active = sceneIdx === i;
return (
{String(i + 1).padStart(2, '0')} · {l}
{i < 2 && }
);
})}
{/* Counter */}
WAVEPIX · 2026
{/* Service 1 — Web */}
}
desc={"브랜드를 빠르게 살리는 홈페이지 구축과 안정적인 운영·유지보수를 한 팀에서 제공합니다."}
>
{/* Service 2 — Security */}
}
desc={"AI·머신러닝 기반 탐지 엔진이 24/365 실시간으로 위협을 차단하고, 침해 사고에 즉시 대응합니다."}
>
{/* Service 3 — Marketing */}
}
desc={"AI가 키워드 트렌드를 분석하고 콘텐츠 초안을 생성합니다. 검색 노출과 광고 성과를 한 화면에서 측정합니다."}
>
{/* Convergence flash */}
{T > 9.8 && T < 12.5 && (
)}
{/* Logo */}
{/* Slogan */}
AI로 중심을 잡고, 한발 앞서갑니다.
wavepix.co.kr
);
}
// utility: linear color blend in hex
function mixColor(a, b, t) {
const ah = parseInt(a.slice(1), 16);
const bh = parseInt(b.slice(1), 16);
const ar = (ah >> 16) & 255, ag = (ah >> 8) & 255, ab = ah & 255;
const br = (bh >> 16) & 255, bg = (bh >> 8) & 255, bb = bh & 255;
const r = Math.round(ar + (br - ar) * t);
const g = Math.round(ag + (bg - ag) * t);
const bl = Math.round(ab + (bb - ab) * t);
return '#' + [r, g, bl].map(v => v.toString(16).padStart(2, '0')).join('');
}
window.Variant3 = Variant3;