diff --git a/.vscode/settings.json b/.vscode/settings.json index 1b4af39..26f4888 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,6 +52,7 @@ "SSERAFIM", "SULLYOON", "Testpage", + "Toastify", "uuidv", "WONYOUNG", "YUJIN", diff --git a/src/App.jsx b/src/App.jsx index fca596d..1c168cf 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,3 +1,7 @@ +import Lenis from "@studio-freight/lenis"; +import { gsap } from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import { useEffect, useRef } from "react"; import { Route, Routes } from "react-router-dom"; import Toastify from "./components/Toastify"; import { CreditProvider } from "./context/CreditContext"; @@ -10,6 +14,30 @@ import NotFound from "./pages/NotFound/index.jsx"; import Testpage from "./pages/Testpage/index.jsx"; function App() { + const lenisRef = useRef(null); + + // Lenis 스크롤 초기화 + useEffect(() => { + lenisRef.current = new Lenis({ + smoothWheel: true, + duration: 1.2, + }); + + lenisRef.current.on("scroll", ScrollTrigger.update); + + const animate = (time) => { + lenisRef.current?.raf(time * 1000); + }; + + gsap.ticker.add(animate); + gsap.ticker.lagSmoothing(0); + + return () => { + gsap.ticker.remove(animate); + lenisRef.current?.destroy(); + }; + }, []); + return ( diff --git a/src/components/Modal/index.jsx b/src/components/Modal/index.jsx index 184f143..d5444db 100644 --- a/src/components/Modal/index.jsx +++ b/src/components/Modal/index.jsx @@ -100,7 +100,8 @@ const Modal = ({ {/* Modal Content */} - {children} + {/* 모달 오픈시 lenis스크롤 막는 data-lenis-prevent 속성 추가 */} +
{children}
diff --git a/src/pages/Landing/components/LandingBottomSection.jsx b/src/pages/Landing/components/LandingBottomSection.jsx index 1a4bb2e..9657d17 100644 --- a/src/pages/Landing/components/LandingBottomSection.jsx +++ b/src/pages/Landing/components/LandingBottomSection.jsx @@ -207,7 +207,7 @@ const StyledLandingBottomSection = styled.section` } } @media all and (max-width: 425px) { - height: 133.33vw !important; + height: 520px !important; .landingGrid { > img { width: 65.07vw; diff --git a/src/pages/Landing/components/LandingMiddleSection.jsx b/src/pages/Landing/components/LandingMiddleSection.jsx index 3057d7d..db988c4 100644 --- a/src/pages/Landing/components/LandingMiddleSection.jsx +++ b/src/pages/Landing/components/LandingMiddleSection.jsx @@ -1,87 +1,136 @@ import styled from "@emotion/styled"; import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; export default function LandingMiddleSection() { // LandingMiddleSection 애니메이션 useEffect(() => { - const ctx = gsap.context(() => { - const anim = gsap.timeline({ - scrollTrigger: { - trigger: ".landingMiddleSection", - start: "top top", - end: "+=300%", - pin: true, - scrub: 1, - id: "sec02Trigger", - }, - }); + const ctx = + window.innerWidth < 768 + ? gsap.context(() => { + // 모바일 디바이스에서 각 article에 대한 애니메이션 + const articles = document.querySelectorAll( + ".landingMiddleSection article", + ); - gsap.set(".landingMiddleSection article:nth-of-type(2)", { - y: "100%", - }); - gsap.set(".landingMiddleSection article:nth-of-type(3)", { - y: "100%", - }); + for (const article of articles) { + const fadeInRight = article.querySelector(".fade-in-right"); + const fadeInUp = article.querySelectorAll(".fade-in-up"); - // 네비게이션 초기 설정 - const navButtons = document.querySelectorAll( - ".landingMiddleSectionNav button", - ); - navButtons[0].classList.add("active"); + if (fadeInRight || fadeInUp) { + gsap + .timeline({ + scrollTrigger: { + trigger: article, + start: "top 50%", + end: "bottom 50%", + }, + }) + .fromTo( + fadeInUp, + { + y: 30, + opacity: 0, + }, + { + y: 0, + opacity: 1, + duration: 0.5, + }, + ) + .fromTo( + fadeInRight, + { + x: 100, + opacity: 0, + }, + { + x: 0, + opacity: 1, + duration: 0.5, + }, + ); + } + } + }) + : gsap.context(() => { + const anim = gsap.timeline({ + scrollTrigger: { + trigger: ".landingMiddleSection", + start: "top top", + end: "+=300%", + pin: true, + scrub: 1, + id: "sec02Trigger", + }, + }); - // 네비게이션 활성화 - const st = ScrollTrigger.create({ - trigger: ".landingMiddleSection", - start: "top top", - end: "+=300%", - onUpdate: (self) => { - const progress = self.progress; - for (const btn of navButtons) { - btn.classList.remove("active"); - } + gsap.set(".landingMiddleSection article:nth-of-type(2)", { + y: "100%", + }); + gsap.set(".landingMiddleSection article:nth-of-type(3)", { + y: "100%", + }); - if (progress < 0.33) { + // 네비게이션 초기 설정 + const navButtons = document.querySelectorAll( + ".landingMiddleSectionNav button", + ); navButtons[0].classList.add("active"); - } else if (progress < 0.66) { - navButtons[1].classList.add("active"); - } else { - navButtons[2].classList.add("active"); - } - }, - }); - // 네비게이션 클릭 이벤트 - navButtons.forEach((btn, index) => { - btn.addEventListener("click", () => { - const targetProgress = index * 0.5; - const targetScroll = st.start + (st.end - st.start) * targetProgress; + // 네비게이션 활성화 + const st = ScrollTrigger.create({ + trigger: ".landingMiddleSection", + start: "top top", + end: "+=300%", + onUpdate: (self) => { + const progress = self.progress; + for (const btn of navButtons) { + btn.classList.remove("active"); + } - gsap.to(window, { - scrollTo: targetScroll, - duration: 1, - ease: "power2.inOut", - }); - }); - }); + if (progress < 0.33) { + navButtons[0].classList.add("active"); + } else if (progress < 0.66) { + navButtons[1].classList.add("active"); + } else { + navButtons[2].classList.add("active"); + } + }, + }); + + // 네비게이션 클릭 이벤트 + navButtons.forEach((btn, index) => { + btn.addEventListener("click", () => { + const targetProgress = index * 0.5; + const targetScroll = + st.start + (st.end - st.start) * targetProgress; + + gsap.to(window, { + scrollTo: targetScroll, + duration: 1, + ease: "power2.inOut", + }); + }); + }); - anim - .to(".landingMiddleSection article:nth-of-type(2)", { - y: 0, - delay: 0.5, - duration: 1, - ease: "power1.inOut", - }) - .to(".landingMiddleSection article:nth-of-type(3)", { - y: 0, - duration: 1, - ease: "power1.inOut", - }) - .to(".landingMiddleSection", { - delay: 0.2, - }); - }); + anim + .to(".landingMiddleSection article:nth-of-type(2)", { + y: 0, + delay: 0.5, + duration: 1, + ease: "power1.inOut", + }) + .to(".landingMiddleSection article:nth-of-type(3)", { + y: 0, + duration: 1, + ease: "power1.inOut", + }) + .to(".landingMiddleSection", { + delay: 0.2, + }); + }); return () => ctx.revert(); }, []); @@ -95,7 +144,7 @@ export default function LandingMiddleSection() {
-

+

마음
닿는 순간,
응원
@@ -104,10 +153,10 @@ export default function LandingMiddleSection() {

-
+
-

+

좋아하는 아티스트에게 직접 후원하고
팬심을 행동으로 보여주세요. @@ -120,7 +169,7 @@ export default function LandingMiddleSection() {

-

+

이번 달
가장 빛난 별
누구? @@ -128,10 +177,10 @@ export default function LandingMiddleSection() {

-
+
-

+

팬들의 사랑을 가장 많이 받은
이달의 아티스트를 소개합니다! @@ -144,7 +193,7 @@ export default function LandingMiddleSection() {

-

+

내 마음 속 @@ -158,10 +207,10 @@ export default function LandingMiddleSection() {

-
+
-

+

내가 좋아하는 아티스트 소식만 골라보고
새로운 콘텐츠와 스케줄도 한눈에!
@@ -173,7 +222,6 @@ export default function LandingMiddleSection() { ); } - const StyledLandingMiddleSection = styled.section` position: relative; @@ -363,6 +411,7 @@ const StyledLandingMiddleSection = styled.section` } } @media all and (max-width: 768px) { + height: auto !important; .landingGrid { gap: 20px; flex-direction: column; @@ -385,9 +434,13 @@ const StyledLandingMiddleSection = styled.section` width: 40vw; top: 70%; img { - max-height: calc(65vh - 100px); + max-height: 400px; } } + article { + position: static; + height: 850px; + } } @media all and (max-width: 425px) { span.show-425 { diff --git a/src/pages/Landing/index.jsx b/src/pages/Landing/index.jsx index 52f6f1a..1bc602a 100644 --- a/src/pages/Landing/index.jsx +++ b/src/pages/Landing/index.jsx @@ -1,8 +1,4 @@ import styled from "@emotion/styled"; -import Lenis from "@studio-freight/lenis"; -import { gsap } from "gsap"; -import { ScrollTrigger } from "gsap/ScrollTrigger"; -import { useEffect, useRef } from "react"; import LandingBottomSection from "./components/LandingBottomSection"; import LandingFooter from "./components/LandingFooter"; import LandingHeader from "./components/LandingHeader"; @@ -10,30 +6,6 @@ import LandingMiddleSection from "./components/LandingMiddleSection"; import LandingTopSection from "./components/LandingTopSection"; const Landing = () => { - const lenisRef = useRef(null); - - // Lenis 스크롤 초기화 - useEffect(() => { - lenisRef.current = new Lenis({ - smoothWheel: true, - duration: 1.2, - }); - - lenisRef.current.on("scroll", ScrollTrigger.update); - - const animate = (time) => { - lenisRef.current?.raf(time * 1000); - }; - - gsap.ticker.add(animate); - gsap.ticker.lagSmoothing(0); - - return () => { - gsap.ticker.remove(animate); - lenisRef.current?.destroy(); - }; - }, []); - return ( @@ -50,7 +22,7 @@ export default Landing; const LandingContainer = styled.div` overflow: hidden; section { - height: 100vh; + height: 100lvh; position: relative; } `;