From d87f2c85bf1c7079c99b132dc5f1aeb98ad7c7db Mon Sep 17 00:00:00 2001 From: sumin Date: Mon, 28 Apr 2025 17:07:50 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=92=84=20Refactor:=20smooth=20scroll?= =?UTF-8?q?=20=EC=A0=84=EC=B2=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit smooth scroll 모든 페이지에 적용 --- .vscode/settings.json | 1 + src/App.jsx | 28 ++++++++++++++++++++++++++++ src/pages/Landing/index.jsx | 28 ---------------------------- 3 files changed, 29 insertions(+), 28 deletions(-) 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/pages/Landing/index.jsx b/src/pages/Landing/index.jsx index 52f6f1a..14e9def 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 ( From cbe40bc89b2cc558aadeb2d6f5135fd0e32fea90 Mon Sep 17 00:00:00 2001 From: sumin Date: Mon, 28 Apr 2025 18:28:49 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Fix:=20smooth=20scroll?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=20=EC=98=A4=ED=94=88=EC=8B=9C=20=EB=A7=89?= =?UTF-8?q?=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit smooth scroll 모달 오픈시data-lenis-prevent 속성 추가하여 막기 처리 --- src/components/Modal/index.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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}
From a4f06020cdd3abef08909e587b6d4959de9d4e15 Mon Sep 17 00:00:00 2001 From: sumin Date: Mon, 28 Apr 2025 19:42:54 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Fix:=20=EB=AA=A8?= =?UTF-8?q?=EB=B0=94=EC=9D=BC=20=EB=94=94=EB=B0=94=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=EB=B3=84=20=EC=83=81=EC=9D=B4=ED=95=9C=20=ED=83=AD=EA=B3=BC=20?= =?UTF-8?q?=EC=A3=BC=EC=86=8C=EC=B0=BD=EC=97=90=20=EC=9D=98=ED=95=9C=20ui?= =?UTF-8?q?=20=EB=AC=B4=EB=84=88=EC=A7=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 모바일 디바이스별 상이한 탭과 주소창에 대응해 애니메이션 수정 --- .../components/LandingBottomSection.jsx | 2 +- .../components/LandingMiddleSection.jsx | 213 +++++++++++------- src/pages/Landing/index.jsx | 2 +- 3 files changed, 135 insertions(+), 82 deletions(-) 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 14e9def..1bc602a 100644 --- a/src/pages/Landing/index.jsx +++ b/src/pages/Landing/index.jsx @@ -22,7 +22,7 @@ export default Landing; const LandingContainer = styled.div` overflow: hidden; section { - height: 100vh; + height: 100lvh; position: relative; } `;