diff --git a/app/services/website/samples/interior/page.tsx b/app/services/website/samples/interior/page.tsx index f126757..874db76 100644 --- a/app/services/website/samples/interior/page.tsx +++ b/app/services/website/samples/interior/page.tsx @@ -1,7 +1,7 @@ 'use client'; import Link from 'next/link'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; /* ── DATA ── */ const portfolio = [ @@ -85,20 +85,111 @@ const ArrowRight = ({ color = '#8B6914' }: { color?: string }) => ( ); /* ── COMPONENT ── */ +const FRAME_COUNT = 48; + export default function InteriorSample() { const [scrolled, setScrolled] = useState(false); + const canvasRef = useRef(null); + const scrollSectionRef = useRef(null); + const textOverlayRef = useRef(null); + const framesRef = useRef([]); useEffect(() => { - const onScroll = () => setScrolled(window.scrollY > 80); - window.addEventListener('scroll', onScroll, { passive: true }); + /* ── NAV scroll state ── */ + const onNavScroll = () => setScrolled(window.scrollY > 80); + window.addEventListener('scroll', onNavScroll, { passive: true }); + /* ── Reveal animations ── */ const observer = new IntersectionObserver( (entries) => entries.forEach((e) => { if (e.isIntersecting) e.target.classList.add('au-visible'); }), { threshold: 0.08 } ); document.querySelectorAll('.au-reveal').forEach((el) => observer.observe(el)); - return () => { window.removeEventListener('scroll', onScroll); observer.disconnect(); }; + /* ── Frame preloading ── */ + const frames: HTMLImageElement[] = new Array(FRAME_COUNT); + framesRef.current = frames; + let firstLoaded = false; + + const drawFrame = (index: number) => { + const canvas = canvasRef.current; + const img = frames[Math.max(0, Math.min(FRAME_COUNT - 1, index))]; + if (!canvas || !img?.complete || !img.naturalWidth) return; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + const cw = canvas.width, ch = canvas.height; + const scale = Math.max(cw / img.naturalWidth, ch / img.naturalHeight); + const dx = (cw - img.naturalWidth * scale) / 2; + const dy = (ch - img.naturalHeight * scale) / 2; + ctx.clearRect(0, 0, cw, ch); + ctx.drawImage(img, dx, dy, img.naturalWidth * scale, img.naturalHeight * scale); + }; + + for (let i = 0; i < FRAME_COUNT; i++) { + const img = new Image(); + img.src = `/interior-frames/frame-${String(i + 1).padStart(3, '0')}.webp`; + img.onload = () => { + if (!firstLoaded) { firstLoaded = true; drawFrame(0); } + }; + frames[i] = img; + } + + /* ── Canvas resize ── */ + const resizeCanvas = () => { + const canvas = canvasRef.current; + if (!canvas) return; + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + drawFrame(0); + }; + resizeCanvas(); + window.addEventListener('resize', resizeCanvas); + + /* ── Scroll scrubbing ── */ + const textMilestones = [ + { start: 0, end: 0.33, idx: 0 }, + { start: 0.33, end: 0.66, idx: 1 }, + { start: 0.66, end: 1.0, idx: 2 }, + ]; + + let rafId = 0; + const onScrub = () => { + cancelAnimationFrame(rafId); + rafId = requestAnimationFrame(() => { + const section = scrollSectionRef.current; + if (!section) return; + const rect = section.getBoundingClientRect(); + const scrollable = section.offsetHeight - window.innerHeight; + if (scrollable <= 0) return; + const progress = Math.max(0, Math.min(1, -rect.top / scrollable)); + const frameIdx = Math.floor(progress * (FRAME_COUNT - 1)); + drawFrame(frameIdx); + + // Update text overlay visibility via DOM (no re-render) + const overlayEl = textOverlayRef.current; + if (!overlayEl) return; + const els = overlayEl.querySelectorAll('[data-milestone]'); + els.forEach((el) => { + const s = parseFloat(el.dataset.start ?? '0'); + const e2 = parseFloat(el.dataset.end ?? '1'); + const vis = progress >= s && progress < e2; + el.style.opacity = vis ? '1' : '0'; + el.style.transform = vis ? 'translateY(0)' : `translateY(${progress < s ? '1.5rem' : '-1.5rem'})`; + }); + // Update progress bar + const bar = overlayEl.querySelector('[data-progress-bar]'); + if (bar) bar.style.width = `${progress * 100}%`; + }); + }; + window.addEventListener('scroll', onScrub, { passive: true }); + + return () => { + window.removeEventListener('scroll', onNavScroll); + window.removeEventListener('scroll', onScrub); + window.removeEventListener('resize', resizeCanvas); + observer.disconnect(); + cancelAnimationFrame(rafId); + }; }, []); const NAV_H = 72; @@ -166,6 +257,43 @@ export default function InteriorSample() { .au-step-num { font-family: 'Playfair Display', Georgia, serif; font-size: 48px; font-weight: 700; color: rgba(139,105,20,0.15); line-height: 1; margin-bottom: 12px; } [style*='word-break'] { word-break: keep-all; } + + /* ── Scroll animation section ── */ + .au-scrub-text { + position: absolute; + opacity: 0; + transition: opacity 0.6s cubic-bezier(0.16,1,0.3,1), transform 0.6s cubic-bezier(0.16,1,0.3,1); + pointer-events: none; + will-change: opacity, transform; + } + .au-scrub-progress { + position: absolute; + bottom: 48px; left: 80px; right: 80px; + height: 1px; + background: rgba(250,248,245,0.12); + } + .au-scrub-progress-fill { + height: 100%; + background: rgba(139,105,20,0.7); + width: 0%; + transition: width 0.1s linear; + } + + /* ── Hero video ── */ + @keyframes au-hero-drift { + from { transform: scale(1.06); } + to { transform: scale(1.0); } + } + .au-hero-video { + position: absolute; inset: 0; + width: 100%; height: 100%; + object-fit: cover; + animation: au-hero-drift 8s cubic-bezier(0.16,1,0.3,1) forwards; + } + + @media (max-width: 768px) { + .au-scrub-progress { left: 24px; right: 24px; bottom: 32px; } + } `}} /> {/* ── BACK BANNER ── */} @@ -214,89 +342,171 @@ export default function InteriorSample() { - {/* ── HERO ── Split 60/40 */} -
- {/* Left — Text */} -
- {/* Grain texture */} -
+ {/* ── HERO ── Full-bleed video background */} +
+ {/* Background video */} +
+ + {/* ── SCROLL ANIMATION — Frame Scrubbing ── */} +
+
+ {/* Canvas: frame-by-frame WebP playback */} + + + {/* Gradient overlays for readability */} +
+
+ + {/* Text overlays — controlled by scroll scrubbing */} +
+ + {/* Milestone 1: 0–33% */} +
+

공간 철학

+

+ 공간이 바뀌면
+ 일상이 바뀝니다. +

+

+ 우리는 단순한 시공을 넘어, 당신이 매일 경험하는 삶의 배경을 설계합니다. +

+
+ + {/* Milestone 2: 33–66% */} +
+

실적

+

+ 12년간
+ 247개의 공간. +

+
+ {[['247+', '완공 프로젝트'], ['4.96', '고객 만족도'], ['98%', '재의뢰율']].map(([n, l]) => ( +
+
{n}
+
{l}
+
+ ))} +
+
+ + {/* Milestone 3: 66–100% */} +
+

지금 시작하세요

+

+ 당신의 공간
+ 이야기를 시작하세요. +

+ + + + + 무료 상담 신청 + +
+
+ + {/* Progress bar — bottom */} +
+
+
+ + {/* Frame counter — bottom right */} +
+ 스크롤로 영상 탐색
diff --git a/public/interior-frames/frame-001.webp b/public/interior-frames/frame-001.webp new file mode 100644 index 0000000..fae2150 Binary files /dev/null and b/public/interior-frames/frame-001.webp differ diff --git a/public/interior-frames/frame-002.webp b/public/interior-frames/frame-002.webp new file mode 100644 index 0000000..ecdbe5f Binary files /dev/null and b/public/interior-frames/frame-002.webp differ diff --git a/public/interior-frames/frame-003.webp b/public/interior-frames/frame-003.webp new file mode 100644 index 0000000..d3a6e24 Binary files /dev/null and b/public/interior-frames/frame-003.webp differ diff --git a/public/interior-frames/frame-004.webp b/public/interior-frames/frame-004.webp new file mode 100644 index 0000000..b35631f Binary files /dev/null and b/public/interior-frames/frame-004.webp differ diff --git a/public/interior-frames/frame-005.webp b/public/interior-frames/frame-005.webp new file mode 100644 index 0000000..cc2d401 Binary files /dev/null and b/public/interior-frames/frame-005.webp differ diff --git a/public/interior-frames/frame-006.webp b/public/interior-frames/frame-006.webp new file mode 100644 index 0000000..94b58fb Binary files /dev/null and b/public/interior-frames/frame-006.webp differ diff --git a/public/interior-frames/frame-007.webp b/public/interior-frames/frame-007.webp new file mode 100644 index 0000000..86394ea Binary files /dev/null and b/public/interior-frames/frame-007.webp differ diff --git a/public/interior-frames/frame-008.webp b/public/interior-frames/frame-008.webp new file mode 100644 index 0000000..3a0fc4e Binary files /dev/null and b/public/interior-frames/frame-008.webp differ diff --git a/public/interior-frames/frame-009.webp b/public/interior-frames/frame-009.webp new file mode 100644 index 0000000..03b9dca Binary files /dev/null and b/public/interior-frames/frame-009.webp differ diff --git a/public/interior-frames/frame-010.webp b/public/interior-frames/frame-010.webp new file mode 100644 index 0000000..3d31c25 Binary files /dev/null and b/public/interior-frames/frame-010.webp differ diff --git a/public/interior-frames/frame-011.webp b/public/interior-frames/frame-011.webp new file mode 100644 index 0000000..6a54ea7 Binary files /dev/null and b/public/interior-frames/frame-011.webp differ diff --git a/public/interior-frames/frame-012.webp b/public/interior-frames/frame-012.webp new file mode 100644 index 0000000..a80f88e Binary files /dev/null and b/public/interior-frames/frame-012.webp differ diff --git a/public/interior-frames/frame-013.webp b/public/interior-frames/frame-013.webp new file mode 100644 index 0000000..31f4343 Binary files /dev/null and b/public/interior-frames/frame-013.webp differ diff --git a/public/interior-frames/frame-014.webp b/public/interior-frames/frame-014.webp new file mode 100644 index 0000000..621a056 Binary files /dev/null and b/public/interior-frames/frame-014.webp differ diff --git a/public/interior-frames/frame-015.webp b/public/interior-frames/frame-015.webp new file mode 100644 index 0000000..fd81202 Binary files /dev/null and b/public/interior-frames/frame-015.webp differ diff --git a/public/interior-frames/frame-016.webp b/public/interior-frames/frame-016.webp new file mode 100644 index 0000000..4a15e0f Binary files /dev/null and b/public/interior-frames/frame-016.webp differ diff --git a/public/interior-frames/frame-017.webp b/public/interior-frames/frame-017.webp new file mode 100644 index 0000000..11dd1d9 Binary files /dev/null and b/public/interior-frames/frame-017.webp differ diff --git a/public/interior-frames/frame-018.webp b/public/interior-frames/frame-018.webp new file mode 100644 index 0000000..c63e6d6 Binary files /dev/null and b/public/interior-frames/frame-018.webp differ diff --git a/public/interior-frames/frame-019.webp b/public/interior-frames/frame-019.webp new file mode 100644 index 0000000..84c0cd7 Binary files /dev/null and b/public/interior-frames/frame-019.webp differ diff --git a/public/interior-frames/frame-020.webp b/public/interior-frames/frame-020.webp new file mode 100644 index 0000000..9824d70 Binary files /dev/null and b/public/interior-frames/frame-020.webp differ diff --git a/public/interior-frames/frame-021.webp b/public/interior-frames/frame-021.webp new file mode 100644 index 0000000..29a2839 Binary files /dev/null and b/public/interior-frames/frame-021.webp differ diff --git a/public/interior-frames/frame-022.webp b/public/interior-frames/frame-022.webp new file mode 100644 index 0000000..8d52faf Binary files /dev/null and b/public/interior-frames/frame-022.webp differ diff --git a/public/interior-frames/frame-023.webp b/public/interior-frames/frame-023.webp new file mode 100644 index 0000000..3d15225 Binary files /dev/null and b/public/interior-frames/frame-023.webp differ diff --git a/public/interior-frames/frame-024.webp b/public/interior-frames/frame-024.webp new file mode 100644 index 0000000..f906b25 Binary files /dev/null and b/public/interior-frames/frame-024.webp differ diff --git a/public/interior-frames/frame-025.webp b/public/interior-frames/frame-025.webp new file mode 100644 index 0000000..2aa7943 Binary files /dev/null and b/public/interior-frames/frame-025.webp differ diff --git a/public/interior-frames/frame-026.webp b/public/interior-frames/frame-026.webp new file mode 100644 index 0000000..8866307 Binary files /dev/null and b/public/interior-frames/frame-026.webp differ diff --git a/public/interior-frames/frame-027.webp b/public/interior-frames/frame-027.webp new file mode 100644 index 0000000..4a8c528 Binary files /dev/null and b/public/interior-frames/frame-027.webp differ diff --git a/public/interior-frames/frame-028.webp b/public/interior-frames/frame-028.webp new file mode 100644 index 0000000..6539582 Binary files /dev/null and b/public/interior-frames/frame-028.webp differ diff --git a/public/interior-frames/frame-029.webp b/public/interior-frames/frame-029.webp new file mode 100644 index 0000000..9ba2b3b Binary files /dev/null and b/public/interior-frames/frame-029.webp differ diff --git a/public/interior-frames/frame-030.webp b/public/interior-frames/frame-030.webp new file mode 100644 index 0000000..cd41b44 Binary files /dev/null and b/public/interior-frames/frame-030.webp differ diff --git a/public/interior-frames/frame-031.webp b/public/interior-frames/frame-031.webp new file mode 100644 index 0000000..325dbb2 Binary files /dev/null and b/public/interior-frames/frame-031.webp differ diff --git a/public/interior-frames/frame-032.webp b/public/interior-frames/frame-032.webp new file mode 100644 index 0000000..e7423cd Binary files /dev/null and b/public/interior-frames/frame-032.webp differ diff --git a/public/interior-frames/frame-033.webp b/public/interior-frames/frame-033.webp new file mode 100644 index 0000000..086a986 Binary files /dev/null and b/public/interior-frames/frame-033.webp differ diff --git a/public/interior-frames/frame-034.webp b/public/interior-frames/frame-034.webp new file mode 100644 index 0000000..f8cdeab Binary files /dev/null and b/public/interior-frames/frame-034.webp differ diff --git a/public/interior-frames/frame-035.webp b/public/interior-frames/frame-035.webp new file mode 100644 index 0000000..9d2e15c Binary files /dev/null and b/public/interior-frames/frame-035.webp differ diff --git a/public/interior-frames/frame-036.webp b/public/interior-frames/frame-036.webp new file mode 100644 index 0000000..f42105c Binary files /dev/null and b/public/interior-frames/frame-036.webp differ diff --git a/public/interior-frames/frame-037.webp b/public/interior-frames/frame-037.webp new file mode 100644 index 0000000..2fc5988 Binary files /dev/null and b/public/interior-frames/frame-037.webp differ diff --git a/public/interior-frames/frame-038.webp b/public/interior-frames/frame-038.webp new file mode 100644 index 0000000..9a6e07d Binary files /dev/null and b/public/interior-frames/frame-038.webp differ diff --git a/public/interior-frames/frame-039.webp b/public/interior-frames/frame-039.webp new file mode 100644 index 0000000..ad5f652 Binary files /dev/null and b/public/interior-frames/frame-039.webp differ diff --git a/public/interior-frames/frame-040.webp b/public/interior-frames/frame-040.webp new file mode 100644 index 0000000..e46eb7d Binary files /dev/null and b/public/interior-frames/frame-040.webp differ diff --git a/public/interior-frames/frame-041.webp b/public/interior-frames/frame-041.webp new file mode 100644 index 0000000..787e9a7 Binary files /dev/null and b/public/interior-frames/frame-041.webp differ diff --git a/public/interior-frames/frame-042.webp b/public/interior-frames/frame-042.webp new file mode 100644 index 0000000..01ee37e Binary files /dev/null and b/public/interior-frames/frame-042.webp differ diff --git a/public/interior-frames/frame-043.webp b/public/interior-frames/frame-043.webp new file mode 100644 index 0000000..aed8cb1 Binary files /dev/null and b/public/interior-frames/frame-043.webp differ diff --git a/public/interior-frames/frame-044.webp b/public/interior-frames/frame-044.webp new file mode 100644 index 0000000..a27ad0e Binary files /dev/null and b/public/interior-frames/frame-044.webp differ diff --git a/public/interior-frames/frame-045.webp b/public/interior-frames/frame-045.webp new file mode 100644 index 0000000..ecac9b5 Binary files /dev/null and b/public/interior-frames/frame-045.webp differ diff --git a/public/interior-frames/frame-046.webp b/public/interior-frames/frame-046.webp new file mode 100644 index 0000000..5101fdb Binary files /dev/null and b/public/interior-frames/frame-046.webp differ diff --git a/public/interior-frames/frame-047.webp b/public/interior-frames/frame-047.webp new file mode 100644 index 0000000..1be05dc Binary files /dev/null and b/public/interior-frames/frame-047.webp differ diff --git a/public/interior-frames/frame-048.webp b/public/interior-frames/frame-048.webp new file mode 100644 index 0000000..270ce13 Binary files /dev/null and b/public/interior-frames/frame-048.webp differ diff --git a/public/interior-hero.mp4 b/public/interior-hero.mp4 new file mode 100644 index 0000000..2dc50ae Binary files /dev/null and b/public/interior-hero.mp4 differ