From cf1b608e4266ca4302863114939a86c5e818cfb8 Mon Sep 17 00:00:00 2001 From: toothlessdev Date: Sat, 6 Sep 2025 14:46:20 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20useFontLoadState=20=ED=9B=85=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EC=97=AC=20=ED=8F=B0=ED=8A=B8=20?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=83=81=ED=83=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mosu-app/src/shared/hooks/useFontLoadState.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 mosu-app/src/shared/hooks/useFontLoadState.ts diff --git a/mosu-app/src/shared/hooks/useFontLoadState.ts b/mosu-app/src/shared/hooks/useFontLoadState.ts new file mode 100644 index 00000000..8219c1c7 --- /dev/null +++ b/mosu-app/src/shared/hooks/useFontLoadState.ts @@ -0,0 +1,17 @@ +import { useEffect, useState } from "react"; + +export const useFontLoadState = () => { + const [fontLoaded, setFontLoaded] = useState(false); + + useEffect(() => { + if (document.fonts && document.fonts.ready) { + document.fonts.ready.then(() => { + setFontLoaded(true); + }); + } else { + setTimeout(() => setFontLoaded(true), 100); + } + }, []); + + return { fontLoaded }; +}; From b243af386c180783405859669649b6ded09fec8d Mon Sep 17 00:00:00 2001 From: toothlessdev Date: Sat, 6 Sep 2025 14:46:36 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20YouTubeHeroSection=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mosu-app/src/pages/index.tsx | 6 +- .../src/widgets/home/HeroSection.module.scss | 2 - .../home/YoutubeHeroSection.module.scss | 160 ++++++++++++++++++ .../src/widgets/home/YoutubeHeroSection.tsx | 56 ++++++ 4 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 mosu-app/src/widgets/home/YoutubeHeroSection.module.scss create mode 100644 mosu-app/src/widgets/home/YoutubeHeroSection.tsx diff --git a/mosu-app/src/pages/index.tsx b/mosu-app/src/pages/index.tsx index 17e21c36..4a83b5f8 100644 --- a/mosu-app/src/pages/index.tsx +++ b/mosu-app/src/pages/index.tsx @@ -6,13 +6,14 @@ import { SiteMetadata } from "@/apps/ui/SiteMetadata"; import { CoworkerSectionUnderMobile } from "@/widgets/home/CoworkerSectionUnderMobile"; import { FeatureSection } from "@/widgets/home/FeatureSection"; import { FooterSection } from "@/widgets/home/FooterSection"; -import { HeroSection } from "@/widgets/home/HeroSection"; +// import { HeroSection } from "@/widgets/home/HeroSection"; import { IntroSection } from "@/widgets/home/IntroSection"; import { PartnershipSection } from "@/widgets/home/PartnershipSection"; import { ProblemSection } from "@/widgets/home/ProblemSection"; import { ReviewSection } from "@/widgets/home/ReviewSection"; import { ServiceLocationSection } from "@/widgets/home/ServiceLocationSection"; import { SolutionSection } from "@/widgets/home/SolutionSection"; +import { YoutubeHeroSection } from "@/widgets/home/YoutubeHeroSection"; gsap.registerPlugin(ScrollTrigger); @@ -23,7 +24,8 @@ export default function Home() { title="모의가 아닌 진짜 수능, 모수" content="실제 학교 교실에서 수능과 동일한 시간에, 수험생이 직접 선택한 모의고사를 가져와 응시하는 실전형 수능 시뮬레이션 프로그램입니다." /> - + {/* */} + diff --git a/mosu-app/src/widgets/home/HeroSection.module.scss b/mosu-app/src/widgets/home/HeroSection.module.scss index 36e22511..f816f489 100644 --- a/mosu-app/src/widgets/home/HeroSection.module.scss +++ b/mosu-app/src/widgets/home/HeroSection.module.scss @@ -110,8 +110,6 @@ margin: 0px auto; - @media (max-width: 1023px) { - } @media (max-width: 768px) { padding: 0.5rem; } diff --git a/mosu-app/src/widgets/home/YoutubeHeroSection.module.scss b/mosu-app/src/widgets/home/YoutubeHeroSection.module.scss new file mode 100644 index 00000000..1f1565b3 --- /dev/null +++ b/mosu-app/src/widgets/home/YoutubeHeroSection.module.scss @@ -0,0 +1,160 @@ +.yt_hero_section { + &__wrapper { + position: relative; + + display: flex; + flex-direction: column; + justify-content: center; + + height: calc(100vh - 100px - 70px); + // 100px: header + // 70px: banner + + color: #fff; + background-color: #000; + + overflow: clip; + + @media (max-width: 1023px) { + height: calc(100vh - 60px - 70px); + } + @media (max-width: 768px) { + height: calc(100vh - 60px - 70px); + } + } + + &__video { + width: 100%; + margin: 0px auto; + } + + &__title { + display: flex; + flex-direction: row; + justify-content: center; + + margin: 0px auto; + text-align: center; + + overflow: hidden; + + font-weight: bold; + font-size: 3.75rem; + + @media (max-width: 1023px) { + flex-direction: column; + gap: 0; + + font-size: 3rem; + } + + @media (max-width: 767px) { + gap: 0; + font-size: 2rem; + + line-height: 1.2; + } + } + + &__content { + display: flex; + flex-direction: column; + gap: 1rem; + + width: 100%; + + margin: 0px auto; + + @media (max-width: 768px) { + padding: 0.5rem; + } + + p { + display: flex; + flex-direction: column; + text-align: center; + + width: 100%; + + margin-left: auto; + margin-right: auto; + + font-size: 1.125rem; + + @media (max-width: 767px) { + font-size: 1rem; + } + } + + button { + display: flex; + align-items: center; + gap: 0.75rem; + + width: fit-content; + + margin: 0.5rem auto; + border-radius: 9999px; + padding: 0.5rem 1rem; + + background-color: #fff; + transition: background-color 1s; + + font-size: 1rem; + + @media (max-width: 1023px) { + padding: 0.5rem 1rem; + font-size: 1rem; + } + @media (max-width: 768px) { + padding: 0.5rem 1rem; + font-size: 0.875rem; + } + + &:hover { + cursor: pointer; + background-color: #f3f3f3; + } + + span:first-child { + color: #000; + } + span:last-child { + aspect-ratio: 1 / 1; + height: fit-content; + + padding: 0.25rem; + border-radius: 9999px; + + background-color: #000; + } + } + + footer { + display: flex; + justify-content: center; + + width: 100%; + height: fit-content; + + margin: 0px auto; + + animation: bounceWithOpacity 1.5s infinite; + + @keyframes bounceWithOpacity { + 0% { + transform: translateY(0); + opacity: 1; + } + 50% { + transform: translateY(-10px); + opacity: 0.5; + } + 100% { + transform: translateY(0); + opacity: 1; + } + } + } + } +} diff --git a/mosu-app/src/widgets/home/YoutubeHeroSection.tsx b/mosu-app/src/widgets/home/YoutubeHeroSection.tsx new file mode 100644 index 00000000..41071f5e --- /dev/null +++ b/mosu-app/src/widgets/home/YoutubeHeroSection.tsx @@ -0,0 +1,56 @@ +import { useGSAP } from "@gsap/react"; +import { YouTubeEmbed } from "@next/third-parties/google"; +import gsap from "gsap"; +import { ChevronDown, ChevronRight } from "lucide-react"; +import Link from "next/link"; +import { useRef } from "react"; + +import { useFontLoadState } from "@/shared/hooks/useFontLoadState"; + +import styles from "./YoutubeHeroSection.module.scss"; + +export const YoutubeHeroSection = () => { + const titleRef = useRef(null); + const textRef = useRef(null); + const registerButtonRef = useRef(null); + const { fontLoaded } = useFontLoadState(); + + useGSAP(() => { + if (!fontLoaded) return; + + const timeline = gsap.timeline(); + timeline.fromTo(titleRef.current, { opacity: 0, y: 50 }, { opacity: 1, y: 0, duration: 2, ease: "expo.out" }); + }, [fontLoaded]); + + return ( +
+

+ 당신의 수능을 바꿀  + 마지막 기회 +

+ +
+ +
+ +
+

+ 모의가 아닌 진짜 수능 이용 가이드입니다. +

+ + + + + + +
+
+ ); +};