diff --git a/public/1Stars.png b/public/1Stars.png deleted file mode 100644 index 1dad8e04..00000000 Binary files a/public/1Stars.png and /dev/null differ diff --git a/public/1Stars.webp b/public/1Stars.webp new file mode 100644 index 00000000..7082f36c Binary files /dev/null and b/public/1Stars.webp differ diff --git a/public/2Stars.png b/public/2Stars.png deleted file mode 100644 index 8e3bc8d4..00000000 Binary files a/public/2Stars.png and /dev/null differ diff --git a/public/2Stars.webp b/public/2Stars.webp new file mode 100644 index 00000000..5a0a1ff7 Binary files /dev/null and b/public/2Stars.webp differ diff --git a/public/CocktailDrop_4x.webp b/public/CocktailDrop_4x.webp new file mode 100644 index 00000000..1f400b51 Binary files /dev/null and b/public/CocktailDrop_4x.webp differ diff --git a/src/app/page.tsx b/src/app/page.tsx index 30f266d2..1e94c4b0 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,12 +1,9 @@ -import Landing from '@/domains/shared/components/3d/Landing'; - -import MainSlide from '@/domains/main/components/mainSlide/components/MainSlide'; +import FinalLanding from '@/domains/main/components/FinalLanding'; export default function Home() { return (
- - +
); } diff --git a/src/domains/main/cocktailDrop/CocktailDrop.tsx b/src/domains/main/cocktailDrop/CocktailDrop.tsx new file mode 100644 index 00000000..dc714918 --- /dev/null +++ b/src/domains/main/cocktailDrop/CocktailDrop.tsx @@ -0,0 +1,104 @@ +'use client'; + +import Image from 'next/image'; +import Cocktailcup from '../../../../public/CocktailDrop_4x.webp'; +import { useLayoutEffect, useRef } from 'react'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/all'; + +gsap.registerPlugin(ScrollTrigger); + +function CocktailDrop() { + const containerRef = useRef(null); + const logoRef = useRef(null); + const line1Ref = useRef(null); + const line2Ref = useRef(null); + + useLayoutEffect(() => { + const ctx = gsap.context(() => { + // 양쪽 대각선 줄 들어오기 (line1, line2) + gsap.fromTo( + [line1Ref.current, line2Ref.current], + { + x: (i) => (i === 0 ? '-100%' : '100%'), + opacity: 0, + }, + { + x: '0%', + opacity: 1, + ease: 'power4.out', + duration: 1.2, + stagger: 0.2, + scrollTrigger: { + trigger: containerRef.current, + // markers: true, // ✅ 디버 + start: 'top 95%', + toggleActions: 'restart none none none', + once: false, + }, + } + ); + + // 로고 위에서 아래로 자연스럽게 등장 + gsap.fromTo( + logoRef.current, + { y: -300, opacity: 0 }, + { + y: -40, + opacity: 1, + duration: 3, + ease: 'power3.out', + scrollTrigger: { + trigger: containerRef.current, + // markers: true, // ✅ 디버 + start: 'top 90%', + toggleActions: 'restart none none none', + once: false, + }, + } + ); + ScrollTrigger.refresh(); + }, containerRef); + + return () => ctx.revert(); + }, [containerRef]); + + return ( +
+ {/* 대각선 줄 1 */} +
+ {/* 대각선 줄 2 */} +
+ + {/* 로고 */} +
+ 로고 이미지 +
+ +
+ + {/* 컵 이미지 */} +
+ 칵테일 컵 +
+
+ ); +} + +export default CocktailDrop; diff --git a/src/domains/shared/components/3d/HomeBackground.tsx b/src/domains/main/components/3d/HomeBackground.tsx similarity index 100% rename from src/domains/shared/components/3d/HomeBackground.tsx rename to src/domains/main/components/3d/HomeBackground.tsx diff --git a/src/domains/main/components/3d/HomeLogo.tsx b/src/domains/main/components/3d/HomeLogo.tsx new file mode 100644 index 00000000..b80a8707 --- /dev/null +++ b/src/domains/main/components/3d/HomeLogo.tsx @@ -0,0 +1,14 @@ +import Image from 'next/image'; + +function HomeLogo({ isDesktop }: { isDesktop: boolean }) { + return ( +
+ 로고 이미지 +
+ ); +} + +export default HomeLogo; diff --git a/src/domains/shared/components/3d/HomeModel.tsx b/src/domains/main/components/3d/HomeModel.tsx similarity index 77% rename from src/domains/shared/components/3d/HomeModel.tsx rename to src/domains/main/components/3d/HomeModel.tsx index 4d0f3bce..9d7a0d00 100644 --- a/src/domains/shared/components/3d/HomeModel.tsx +++ b/src/domains/main/components/3d/HomeModel.tsx @@ -3,17 +3,15 @@ import { Environment, OrbitControls, useGLTF } from '@react-three/drei'; import { Canvas, useFrame, useThree } from '@react-three/fiber'; import { Bloom, EffectComposer } from '@react-three/postprocessing'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef } from 'react'; import * as THREE from 'three'; -function Model({ onLoaded }: { onLoaded: () => void }) { - const { scene } = useGLTF('/3d/model/scene.gltf'); - const [scale, setScale] = useState(13); +interface Props { + onLoaded: () => void; +} - useEffect(() => { - const isMobile = window.innerWidth < 768; // 모바일 기준 너비 - setScale(isMobile ? 3.8 : 11.5); // 모바일이면 작게 - }, []); +function Model({ onLoaded }: Props) { + const { scene } = useGLTF('/3d/model/scene.gltf'); useEffect(() => { if (scene) { @@ -41,7 +39,7 @@ function Model({ onLoaded }: { onLoaded: () => void }) { } }); - return ; + return ; } function CameraAnimation() { @@ -61,16 +59,20 @@ function CameraAnimation() { return null; } -function HomeModel({ onLoaded }: { onLoaded: () => void }) { +function HomeModel({ onLoaded }: Props) { return ( - - + + - + - + {/* */} + {!isDesktop ? ( +

+ 어떤 칵테일이 끌리시나요? SSoul이 쉽게 골라드릴게요. +

+ ) : ( +

+ 어떤 칵테일이 끌리시나요? SSoul이 쉽게 골라드릴게요. +

+ )} + + ); +} + +export default HomeText; diff --git a/src/domains/main/components/3d/Landing.tsx b/src/domains/main/components/3d/Landing.tsx new file mode 100644 index 00000000..4f480cfb --- /dev/null +++ b/src/domains/main/components/3d/Landing.tsx @@ -0,0 +1,42 @@ +'use client'; + +import HomeModel from './HomeModel'; +import HomeLogo from './HomeLogo'; +import HomeText from './HomeText'; +import Scroll from './Scroll'; +import { useEffect, useState } from 'react'; +import ModelImage from './ModelImage'; + +interface Props { + setIsLoading: (value: boolean) => void; + isDesktop: boolean; +} + +function Landing({ setIsLoading, isDesktop }: Props) { + const [modelLoaded, setModelLoaded] = useState(false); + useEffect(() => { + if (modelLoaded) setIsLoading(false); + }, [modelLoaded, setIsLoading]); + return ( + <> +
+
+ {isDesktop ? ( + setModelLoaded(true)} /> + ) : ( + setModelLoaded(true)} /> + )} + {modelLoaded && ( + <> + + + + + )} +
+
+ + ); +} + +export default Landing; diff --git a/src/domains/main/components/3d/ModelImage.tsx b/src/domains/main/components/3d/ModelImage.tsx new file mode 100644 index 00000000..1f37f1e6 --- /dev/null +++ b/src/domains/main/components/3d/ModelImage.tsx @@ -0,0 +1,14 @@ +import { useEffect } from 'react'; + +interface Props { + onLoaded: () => void; +} + +function ModelImage({ onLoaded }: Props) { + useEffect(() => { + onLoaded(); + }); + return
; +} + +export default ModelImage; diff --git a/src/domains/main/components/3d/Scroll.tsx b/src/domains/main/components/3d/Scroll.tsx new file mode 100644 index 00000000..0f289fe2 --- /dev/null +++ b/src/domains/main/components/3d/Scroll.tsx @@ -0,0 +1,23 @@ +import Lottie from 'lottie-react'; +import scroll from '@/shared/assets/lottie/ScrollDownAnimation.json'; + +function Scroll({ isDesktop }: { isDesktop: boolean }) { + const style = !isDesktop + ? { + width: 45, + height: 45, + } + : { + width: 60, + height: 60, + }; + return ( +
+
+ +
+
+ ); +} + +export default Scroll; diff --git a/src/domains/main/components/3d/StarMain.tsx b/src/domains/main/components/3d/StarMain.tsx new file mode 100644 index 00000000..575ef286 --- /dev/null +++ b/src/domains/main/components/3d/StarMain.tsx @@ -0,0 +1,96 @@ +'use client'; + +import Image from 'next/image'; +import foreStar from '../../../../../public/1Stars.webp'; +import backStar from '../../../../../public/2Stars.webp'; +import { useEffect, useRef } from 'react'; +import gsap from 'gsap'; + +function StarMain() { + // const background = useRef(null); + // const foreground = useRef(null); + // const mouse = useRef({ x: 0, y: 0 }); + // const rafId = useRef(null); + + // useEffect(() => { + // if (!background.current || !foreground.current) return; + + // const bgX = gsap.quickSetter(background.current, 'x', 'px'); + // const bgY = gsap.quickSetter(background.current, 'y', 'px'); + // const bgRotate = gsap.quickSetter(background.current, 'rotate', 'deg'); + + // const fgX = gsap.quickSetter(foreground.current, 'x', 'px'); + // const fgY = gsap.quickSetter(foreground.current, 'y', 'px'); + // const fgRotate = gsap.quickSetter(foreground.current, 'rotate', 'deg'); + + // const update = () => { + // const { x, y } = mouse.current; + + // bgX(x * -2); + // bgY(y * -2); + // bgRotate(x * -0.2); + + // fgX(x * 3); + // fgY(y * 3); + // fgRotate(y * 0.2); + + // rafId.current = requestAnimationFrame(update); + // }; + + // const handleMouseMove = (e: MouseEvent) => { + // // 화면 중앙 기준으로 얼마나 벗어났는지 (-1 ~ 1 범위) + // const x = (e.clientX / window.innerWidth - 0.5) * 2; + // const y = (e.clientY / window.innerHeight - 0.5) * 2; + // mouse.current = { x, y }; + // }; + + // const handleTouchMove = (e: TouchEvent) => { + // const touch = e.touches[0]; + // const x = (touch.clientX / window.innerWidth - 0.5) * 2; + // const y = (touch.clientY / window.innerHeight - 0.5) * 2; + // mouse.current = { x, y }; + // }; + + // window.addEventListener('mousemove', handleMouseMove); + // window.addEventListener('touchmove', handleTouchMove); + // rafId.current = requestAnimationFrame(update); + + // return () => { + // window.removeEventListener('mousemove', handleMouseMove); + // window.removeEventListener('touchmove', handleTouchMove); + // if (rafId.current) cancelAnimationFrame(rafId.current); + // }; + // }, []); + + return ( + <> + {/* */} +
+
+
+ 앞쪽 별 +
+
+ 뒤쪽 별 +
+
+
+ + ); +} + +export default StarMain; diff --git a/src/domains/main/components/FinalLanding.tsx b/src/domains/main/components/FinalLanding.tsx new file mode 100644 index 00000000..5f79c892 --- /dev/null +++ b/src/domains/main/components/FinalLanding.tsx @@ -0,0 +1,93 @@ +'use client'; +import { useEffect, useRef, useState } from 'react'; +import Landing from './3d/Landing'; +import MainSlide from './mainSlide/components/MainSlide'; +import Spinner from '@/shared/components/spinner/Spinner'; +import gsap from 'gsap'; +import { ScrollSmoother, ScrollTrigger } from 'gsap/all'; +import StarMain from './3d/StarMain'; +import CocktailDrop from '../cocktailDrop/CocktailDrop'; + +function FinalLanding() { + const [isLoading, setIsLoading] = useState(true); + const smootherRef = useRef(null); + + const [isDesktop, setIsDesktop] = useState(false); + + useEffect(() => { + const checkViewport = () => { + setIsDesktop(window.innerWidth >= 1024); + }; + + checkViewport(); + + window.addEventListener('resize', checkViewport); + + return () => { + window.removeEventListener('resize', checkViewport); + }; + }, []); + + useEffect(() => { + gsap.registerPlugin(ScrollTrigger, ScrollSmoother); + + // ScrollSmoother는 클라이언트에서 한 번만 초기화 + if (isDesktop && !smootherRef.current) { + smootherRef.current = ScrollSmoother.create({ + wrapper: '#scroll-wrapper', + content: '#scroll-content', + smooth: 2.0, + normalizeScroll: { + allowNestedScroll: true, + }, + ignoreMobileResize: true, + effects: true, + }); + } + + // ⚠️ 모바일에서 smoother 적용 안 하도록 명시적 분기 + if (!isDesktop && smootherRef.current) { + smootherRef.current.kill(); + smootherRef.current = null; + } + + return () => { + // cleanup: 컴포넌트 언마운트 시 smoother 제거 + smootherRef.current?.kill(); + smootherRef.current = null; + }; + }, [isDesktop]); + + return ( + <> + + {isDesktop ? ( +
+
+ {isLoading && } + + {!isLoading && ( + <> + + + + )} +
+
+ ) : ( +
+ {isLoading && } + + {!isLoading && ( + <> + + + + )} +
+ )} + + ); +} + +export default FinalLanding; diff --git a/src/domains/main/components/mainSlide/components/MainSlide.tsx b/src/domains/main/components/mainSlide/components/MainSlide.tsx index 88085e74..52e2e699 100644 --- a/src/domains/main/components/mainSlide/components/MainSlide.tsx +++ b/src/domains/main/components/mainSlide/components/MainSlide.tsx @@ -8,145 +8,114 @@ import MobileSlide from './mobile/MobileSlide'; import MainSlideIntro from './MainSlideIntro'; import MainSlideTest from './MainSlideTest'; import MainSlideCommunity from './MainSlideCommunity'; -import StarBg from '@/domains/shared/components/star-bg/StarBg'; gsap.registerPlugin(ScrollTrigger); -function MainSlide() { +function MainSlide({ isDesktop }: { isDesktop: boolean }) { const root = useRef(null); - const [isMobile, setIsMobile] = useState(false); + const initialRoot = useRef(null); const [mounted, setMounted] = useState(false); const cleanupFnRef = useRef<(() => void) | null>(null); - const resizeTimeoutRef = useRef(null); - useEffect(() => { - setIsMobile(window.innerWidth < 1024); + useLayoutEffect(() => { setMounted(true); - - const handleResize = () => { - // 디바운스: resize 이벤트를 200ms 지연 - if (resizeTimeoutRef.current) { - clearTimeout(resizeTimeoutRef.current); - } - - resizeTimeoutRef.current = setTimeout(() => { - const newIsMobile = window.innerWidth < 1024; - - // 모바일 ↔ 데스크탑 전환 시에만 cleanup 실행 - if (newIsMobile !== isMobile) { - // GSAP을 먼저 완전히 정리 - if (cleanupFnRef.current) { - cleanupFnRef.current(); - cleanupFnRef.current = null; - } - - // 상태 업데이트 - setIsMobile(newIsMobile); - } else if (!newIsMobile) { - // 데스크탑 내에서의 리사이즈 - ScrollTrigger refresh - ScrollTrigger.refresh(true); - } - }, 200); - }; - - window.addEventListener('resize', handleResize); - return () => { - window.removeEventListener('resize', handleResize); - if (resizeTimeoutRef.current) { - clearTimeout(resizeTimeoutRef.current); - } - if (cleanupFnRef.current) { - cleanupFnRef.current(); - } - }; - }, [isMobile]); + }, []); // GSAP 초기화 - 데스크탑에서만 useLayoutEffect(() => { - if (!mounted) return; - if (isMobile) return; - if (!root.current) return; + if (!mounted || !isDesktop || !root.current) return; const el = root.current; const stage = el.querySelector('.stage') as HTMLElement; if (!stage) return; - const timer = setTimeout(() => { - if (!root.current) return; - - const ctx = gsap.context(() => { - const panels = Array.from(el.querySelectorAll('.panel')); - const tl = gsap.timeline({ paused: true, defaults: { ease: 'power3.inOut' } }); - - panels.forEach((panel, i) => { - const c = panel.querySelector('.slide-content'); - if (!c) return; - const stageW = () => stage.clientWidth; - const contentW = () => c.getBoundingClientRect().width; - - gsap.set(c, { - x: () => stageW(), - immediateRender: false, - }); - - tl.to( - c, - { - x: () => stageW() - contentW(), - duration: 2, - immediateRender: false, - onStart: () => c.classList.remove('invisible'), - }, - i - ); - }); + const ctx = gsap.context(() => { + // 첫 진입 애니메이션 + gsap.fromTo( + initialRoot.current, + { opacity: 0 }, + { + y: 0, + opacity: 1, + ease: 'power5.out', + scrollTrigger: { + trigger: initialRoot.current, + start: 'top 80%', + end: 'top top', + scrub: 0.2, + }, + } + ); - ScrollTrigger.create({ - trigger: el, - start: 'top top', - end: `+=${panels.length * 100}%`, - pin: true, - scrub: true, - animation: tl, - invalidateOnRefresh: true, - }); + const panels = Array.from(el.querySelectorAll('.panel')); + const tl = gsap.timeline({ paused: true, defaults: { ease: 'power3.inOut' } }); - ScrollTrigger.refresh(); - }, root); + panels.forEach((panel, i) => { + const c = panel.querySelector('.slide-content'); + if (!c) return; + const stageW = () => stage.clientWidth; + const contentW = () => c.getBoundingClientRect().width; - cleanupFnRef.current = () => { - const allTriggers = ScrollTrigger.getAll(); - allTriggers.forEach((st) => { - if (st.trigger === el || el.contains(st.trigger as Node)) { - st.kill(true); - } + gsap.set(c, { + x: () => stageW(), + immediateRender: false, }); - try { - ctx.revert(); - } catch {} - - const pinSpacers = document.querySelectorAll('.pin-spacer'); - pinSpacers.forEach((spacer) => { - if (spacer.contains(el) || el.contains(spacer)) { - const child = spacer.querySelector('section'); - if (child && spacer.parentElement) { - spacer.parentElement.appendChild(child); - } - spacer.remove(); + tl.to( + c, + { + x: () => stageW() - contentW(), + duration: 2, + immediateRender: false, + onStart: () => c.classList.remove('invisible'), + }, + i + ); + }); + + ScrollTrigger.create({ + trigger: el, + start: 'top top', + end: `+=${panels.length * 100}%`, + pin: true, + scrub: true, + animation: tl, + invalidateOnRefresh: true, + }); + + ScrollTrigger.refresh(); + }, root); + + cleanupFnRef.current = () => { + const allTriggers = ScrollTrigger.getAll(); + allTriggers.forEach((st) => { + if (st.trigger === el || el.contains(st.trigger as Node)) { + st.kill(true); + } + }); + + try { + ctx.revert(); + } catch {} + + // pin-spacer 정리 + const pinSpacers = document.querySelectorAll('.pin-spacer'); + pinSpacers.forEach((spacer) => { + if (spacer.contains(el) || el.contains(spacer)) { + const child = spacer.querySelector('section'); + if (child && spacer.parentElement) { + spacer.parentElement.appendChild(child); } - }); - }; - }, 50); + spacer.remove(); + } + }); + }; return () => { - clearTimeout(timer); - if (cleanupFnRef.current) { - cleanupFnRef.current(); - cleanupFnRef.current = null; - } + cleanupFnRef.current?.(); + cleanupFnRef.current = null; }; - }, [isMobile, mounted]); + }, [isDesktop, mounted]); // SSR 방지 if (!mounted) { return null; @@ -154,29 +123,27 @@ function MainSlide() { return ( <> - {isMobile ? ( - - - + {!isDesktop ? ( + ) : ( - -
-
-
+
+
+
+
-
+
-
+
-
+
- +
)} ); diff --git a/src/domains/main/components/mainSlide/components/MainSlideAbv.tsx b/src/domains/main/components/mainSlide/components/MainSlideAbv.tsx index 31113543..4870bf08 100644 --- a/src/domains/main/components/mainSlide/components/MainSlideAbv.tsx +++ b/src/domains/main/components/mainSlide/components/MainSlideAbv.tsx @@ -41,28 +41,30 @@ function MainSlideAbv() { ]; return ( -
-
+
+
3 -
-

- 내 알콜도수 UP -

-

- 5도 부터 시작하는 내 알콜도수
글 작성,댓글,좋아요 / 킵으로 알콜도수 UP!
- 알콜도수에 따라 변하는 쑤리(SSURY)를 보는 재미도 있어요. -

-
-
-
    - {SSURY_DRUNK.map(({ id, src, abv }) => ( -
  • - -
  • - ))} -
-
- +
+
+

+ 내 알콜도수 UP +

+

+ 5도 부터 시작하는 내 알콜도수
글 작성,댓글,좋아요 / 킵으로 알콜도수 UP!
+ 알콜도수에 따라 변하는 쑤리(SSURY)를 보는 재미도 있어요. +

+
+
+
    + {SSURY_DRUNK.map(({ id, src, abv }) => ( +
  • + +
  • + ))} +
+
+ +
diff --git a/src/domains/main/components/mainSlide/components/MainSlideCommunity.tsx b/src/domains/main/components/mainSlide/components/MainSlideCommunity.tsx index fc1b4c3b..f8547b2e 100644 --- a/src/domains/main/components/mainSlide/components/MainSlideCommunity.tsx +++ b/src/domains/main/components/mainSlide/components/MainSlideCommunity.tsx @@ -1,9 +1,9 @@ function MainSlideCommunity() { return ( -
-
-
- 2 +
+
+ 2 +

술술 즐기는, 커뮤니티 diff --git a/src/domains/main/components/mainSlide/components/MainSlideIntro.tsx b/src/domains/main/components/mainSlide/components/MainSlideIntro.tsx index 3733a450..2ff790b7 100644 --- a/src/domains/main/components/mainSlide/components/MainSlideIntro.tsx +++ b/src/domains/main/components/mainSlide/components/MainSlideIntro.tsx @@ -1,15 +1,11 @@ -import background from '@/shared/assets/images/cocktailBg.webp'; -import Image from 'next/image'; - function MainSlideIntro() { return (
- -
-

+
+

칵테일
누구나 쉽게 즐길 수 있어요

-

+

SSOUL의 재밌고 다양한 기능들로 더 친근하게 접해보세요

diff --git a/src/domains/main/components/mainSlide/components/MainSlideTest.tsx b/src/domains/main/components/mainSlide/components/MainSlideTest.tsx index 482e2c1e..3f382e9a 100644 --- a/src/domains/main/components/mainSlide/components/MainSlideTest.tsx +++ b/src/domains/main/components/mainSlide/components/MainSlideTest.tsx @@ -15,10 +15,10 @@ const DUMMY_TEST = [ function MainSlideTest() { return ( -
-
+
+
1 -
+

AI기반 취향테스트 diff --git a/src/domains/main/components/mainSlide/components/MainTestDummy.tsx b/src/domains/main/components/mainSlide/components/MainTestDummy.tsx index f7460351..b3db4a5e 100644 --- a/src/domains/main/components/mainSlide/components/MainTestDummy.tsx +++ b/src/domains/main/components/mainSlide/components/MainTestDummy.tsx @@ -48,7 +48,7 @@ function MainTestDummy({ message, option, type }: Props) {

쑤리

{message && ( -
+

{message}

@@ -69,7 +69,7 @@ function MainTestDummy({ message, option, type }: Props) {
)} {type == 'text' && ( -
+
{DUMMY_CARD.map(({ id, src, cocktailName }) => ( ))} diff --git a/src/domains/main/components/mainSlide/components/mobile/MobileSlide.tsx b/src/domains/main/components/mainSlide/components/mobile/MobileSlide.tsx index 11e4c8e3..9f4c8321 100644 --- a/src/domains/main/components/mainSlide/components/mobile/MobileSlide.tsx +++ b/src/domains/main/components/mainSlide/components/mobile/MobileSlide.tsx @@ -1,24 +1,14 @@ -import background from '@/shared/assets/images/cocktailBg.webp'; import MobileSlideTest from './MobileSlideTest'; import MobileSlideCommunity from './MobileSlideCommunity'; import MobileAbv from './MobileAbv'; function MobileSlide() { return ( -
-

+
+

칵테일
누구나 쉽게 즐길 수 있어요

-

+

SSOUL의 재밌고 다양한 기능들로 더 친근하게 접해보세요

@@ -26,7 +16,7 @@ function MobileSlide() {
-

+
); } export default MobileSlide; diff --git a/src/domains/shared/components/3d/Landing.tsx b/src/domains/shared/components/3d/Landing.tsx deleted file mode 100644 index ceded718..00000000 --- a/src/domains/shared/components/3d/Landing.tsx +++ /dev/null @@ -1,24 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import HomeModel from './HomeModel'; -import StarMain from './StarMain'; -import Spinner from '@/shared/components/spinner/Spinner'; - -function Landing() { - const [isLoading, setIsLoading] = useState(true); - - return ( - <> - {isLoading && } -
-
- setIsLoading(false)} /> - {!isLoading && } -
-
- - ); -} - -export default Landing; diff --git a/src/domains/shared/components/3d/StarMain.tsx b/src/domains/shared/components/3d/StarMain.tsx deleted file mode 100644 index 87a00d9c..00000000 --- a/src/domains/shared/components/3d/StarMain.tsx +++ /dev/null @@ -1,102 +0,0 @@ -'use client'; - -import Image from 'next/image'; -import foreStar from '../../../../../public/1Stars.png'; -import backStar from '../../../../../public/2Stars.png'; -import { useEffect, useRef } from 'react'; -import gsap from 'gsap'; - -function StarMain() { - const background = useRef(null); - const foreground = useRef(null); - const mouse = useRef({ x: 0, y: 0 }); - const rafId = useRef(null); - - useEffect(() => { - if (!background.current || !foreground.current) return; - - const bgX = gsap.quickSetter(background.current, 'x', 'px'); - const bgY = gsap.quickSetter(background.current, 'y', 'px'); - const bgRotate = gsap.quickSetter(background.current, 'rotate', 'deg'); - - const fgX = gsap.quickSetter(foreground.current, 'x', 'px'); - const fgY = gsap.quickSetter(foreground.current, 'y', 'px'); - const fgRotate = gsap.quickSetter(foreground.current, 'rotate', 'deg'); - - const update = () => { - const { x, y } = mouse.current; - - bgX(x * -2); - bgY(y * -2); - bgRotate(x * -0.2); - - fgX(x * 3); - fgY(y * 3); - fgRotate(y * 0.2); - - rafId.current = requestAnimationFrame(update); - }; - - const handleMouseMove = (e: MouseEvent) => { - // 화면 중앙 기준으로 얼마나 벗어났는지 (-1 ~ 1 범위) - const x = (e.clientX / window.innerWidth - 0.5) * 2; - const y = (e.clientY / window.innerHeight - 0.5) * 2; - mouse.current = { x, y }; - }; - - const handleTouchMove = (e: TouchEvent) => { - const touch = e.touches[0]; - const x = (touch.clientX / window.innerWidth - 0.5) * 2; - const y = (touch.clientY / window.innerHeight - 0.5) * 2; - mouse.current = { x, y }; - }; - - window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('touchmove', handleTouchMove); - rafId.current = requestAnimationFrame(update); - - return () => { - window.removeEventListener('mousemove', handleMouseMove); - window.removeEventListener('touchmove', handleTouchMove); - if (rafId.current) cancelAnimationFrame(rafId.current); - }; - }, []); - - return ( - <> - {/* */} -
-
-
- 앞쪽 별 -
-
- 뒤쪽 별 -
-
-
- - ); -} - -export default StarMain; diff --git a/src/shared/assets/lottie/ScrollDownAnimation.json b/src/shared/assets/lottie/ScrollDownAnimation.json new file mode 100644 index 00000000..2b64a828 --- /dev/null +++ b/src/shared/assets/lottie/ScrollDownAnimation.json @@ -0,0 +1 @@ +{"v":"5.5.6","fr":29.9700012207031,"ip":0,"op":89.0000036250443,"w":51,"h":130,"nm":"Scroll_icon_white","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Слой 6 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.433,"y":1},"o":{"x":0.717,"y":0.009},"t":0,"s":[25.179,30.476,0],"to":[0,3.604,0],"ti":[0,-3.604,0]},{"t":59.0000024031193,"s":[25.179,52.101,0]}],"ix":2},"a":{"a":0,"k":[0.763,3.624,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[100,100,100]},{"t":59.0000024031193,"s":[100,0,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.763,0.763],[0.763,6.485]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.526,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Слой 10 Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":34,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":51,"s":[100]},{"t":68.0000027696968,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.179,119.784,0],"ix":2},"a":{"a":0,"k":[12.971,7.248,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-11.445,-5.722],[0,5.722],[11.445,-5.722]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.526,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[12.971,7.248],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":34.0000013848484,"op":934.0000380426,"st":34.0000013848484,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Слой 9 Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":34,"s":[100]},{"t":51.0000020772726,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.179,102.284,0],"ix":2},"a":{"a":0,"k":[12.971,7.248,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-11.445,-5.722],[0,5.722],[11.445,-5.722]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.526,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[12.971,7.248],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":17.0000006924242,"op":917.000037350176,"st":17.0000006924242,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Слой 4 Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[100]},{"t":34.0000013848484,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.179,82.784,0],"ix":2},"a":{"a":0,"k":[12.971,7.248,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-11.445,-5.722],[0,5.722],[11.445,-5.722]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.526,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[12.971,7.248],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Слой 5 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.179,46.542,0],"ix":2},"a":{"a":0,"k":[12.208,22.127,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.899,0],[0,0],[0,-5.899],[0,0],[5.899,0],[0,5.899],[0,0]],"o":[[0,0],[5.899,0],[0,0],[0,5.899],[-5.899,0],[0,0],[0,-5.899]],"v":[[0,-20.6],[0,-20.6],[10.682,-9.918],[10.682,9.92],[0,20.6],[-10.682,9.92],[-10.682,-9.918]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.526,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[12.208,22.127],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900.000036657751,"st":0,"bm":0}],"markers":[]} \ No newline at end of file