Skip to content

Commit 01e634a

Browse files
authored
Merge pull request #386 from mosu-dev/feature#385
Feature#385 메인페이지 youtube embed 영상 삽입
2 parents 65f8982 + b243af3 commit 01e634a

5 files changed

Lines changed: 237 additions & 4 deletions

File tree

mosu-app/src/pages/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import { SiteMetadata } from "@/apps/ui/SiteMetadata";
66
import { CoworkerSectionUnderMobile } from "@/widgets/home/CoworkerSectionUnderMobile";
77
import { FeatureSection } from "@/widgets/home/FeatureSection";
88
import { FooterSection } from "@/widgets/home/FooterSection";
9-
import { HeroSection } from "@/widgets/home/HeroSection";
9+
// import { HeroSection } from "@/widgets/home/HeroSection";
1010
import { IntroSection } from "@/widgets/home/IntroSection";
1111
import { PartnershipSection } from "@/widgets/home/PartnershipSection";
1212
import { ProblemSection } from "@/widgets/home/ProblemSection";
1313
import { ReviewSection } from "@/widgets/home/ReviewSection";
1414
import { ServiceLocationSection } from "@/widgets/home/ServiceLocationSection";
1515
import { SolutionSection } from "@/widgets/home/SolutionSection";
16+
import { YoutubeHeroSection } from "@/widgets/home/YoutubeHeroSection";
1617

1718
gsap.registerPlugin(ScrollTrigger);
1819

@@ -23,7 +24,8 @@ export default function Home() {
2324
title="모의가 아닌 진짜 수능, 모수"
2425
content="실제 학교 교실에서 수능과 동일한 시간에, 수험생이 직접 선택한 모의고사를 가져와 응시하는 실전형 수능 시뮬레이션 프로그램입니다."
2526
/>
26-
<HeroSection />
27+
{/* <HeroSection /> */}
28+
<YoutubeHeroSection />
2729
<IntroSection />
2830
<ProblemSection />
2931
<SolutionSection />
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useEffect, useState } from "react";
2+
3+
export const useFontLoadState = () => {
4+
const [fontLoaded, setFontLoaded] = useState(false);
5+
6+
useEffect(() => {
7+
if (document.fonts && document.fonts.ready) {
8+
document.fonts.ready.then(() => {
9+
setFontLoaded(true);
10+
});
11+
} else {
12+
setTimeout(() => setFontLoaded(true), 100);
13+
}
14+
}, []);
15+
16+
return { fontLoaded };
17+
};

mosu-app/src/widgets/home/HeroSection.module.scss

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,6 @@
110110

111111
margin: 0px auto;
112112

113-
@media (max-width: 1023px) {
114-
}
115113
@media (max-width: 768px) {
116114
padding: 0.5rem;
117115
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
.yt_hero_section {
2+
&__wrapper {
3+
position: relative;
4+
5+
display: flex;
6+
flex-direction: column;
7+
justify-content: center;
8+
9+
height: calc(100vh - 100px - 70px);
10+
// 100px: header
11+
// 70px: banner
12+
13+
color: #fff;
14+
background-color: #000;
15+
16+
overflow: clip;
17+
18+
@media (max-width: 1023px) {
19+
height: calc(100vh - 60px - 70px);
20+
}
21+
@media (max-width: 768px) {
22+
height: calc(100vh - 60px - 70px);
23+
}
24+
}
25+
26+
&__video {
27+
width: 100%;
28+
margin: 0px auto;
29+
}
30+
31+
&__title {
32+
display: flex;
33+
flex-direction: row;
34+
justify-content: center;
35+
36+
margin: 0px auto;
37+
text-align: center;
38+
39+
overflow: hidden;
40+
41+
font-weight: bold;
42+
font-size: 3.75rem;
43+
44+
@media (max-width: 1023px) {
45+
flex-direction: column;
46+
gap: 0;
47+
48+
font-size: 3rem;
49+
}
50+
51+
@media (max-width: 767px) {
52+
gap: 0;
53+
font-size: 2rem;
54+
55+
line-height: 1.2;
56+
}
57+
}
58+
59+
&__content {
60+
display: flex;
61+
flex-direction: column;
62+
gap: 1rem;
63+
64+
width: 100%;
65+
66+
margin: 0px auto;
67+
68+
@media (max-width: 768px) {
69+
padding: 0.5rem;
70+
}
71+
72+
p {
73+
display: flex;
74+
flex-direction: column;
75+
text-align: center;
76+
77+
width: 100%;
78+
79+
margin-left: auto;
80+
margin-right: auto;
81+
82+
font-size: 1.125rem;
83+
84+
@media (max-width: 767px) {
85+
font-size: 1rem;
86+
}
87+
}
88+
89+
button {
90+
display: flex;
91+
align-items: center;
92+
gap: 0.75rem;
93+
94+
width: fit-content;
95+
96+
margin: 0.5rem auto;
97+
border-radius: 9999px;
98+
padding: 0.5rem 1rem;
99+
100+
background-color: #fff;
101+
transition: background-color 1s;
102+
103+
font-size: 1rem;
104+
105+
@media (max-width: 1023px) {
106+
padding: 0.5rem 1rem;
107+
font-size: 1rem;
108+
}
109+
@media (max-width: 768px) {
110+
padding: 0.5rem 1rem;
111+
font-size: 0.875rem;
112+
}
113+
114+
&:hover {
115+
cursor: pointer;
116+
background-color: #f3f3f3;
117+
}
118+
119+
span:first-child {
120+
color: #000;
121+
}
122+
span:last-child {
123+
aspect-ratio: 1 / 1;
124+
height: fit-content;
125+
126+
padding: 0.25rem;
127+
border-radius: 9999px;
128+
129+
background-color: #000;
130+
}
131+
}
132+
133+
footer {
134+
display: flex;
135+
justify-content: center;
136+
137+
width: 100%;
138+
height: fit-content;
139+
140+
margin: 0px auto;
141+
142+
animation: bounceWithOpacity 1.5s infinite;
143+
144+
@keyframes bounceWithOpacity {
145+
0% {
146+
transform: translateY(0);
147+
opacity: 1;
148+
}
149+
50% {
150+
transform: translateY(-10px);
151+
opacity: 0.5;
152+
}
153+
100% {
154+
transform: translateY(0);
155+
opacity: 1;
156+
}
157+
}
158+
}
159+
}
160+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { useGSAP } from "@gsap/react";
2+
import { YouTubeEmbed } from "@next/third-parties/google";
3+
import gsap from "gsap";
4+
import { ChevronDown, ChevronRight } from "lucide-react";
5+
import Link from "next/link";
6+
import { useRef } from "react";
7+
8+
import { useFontLoadState } from "@/shared/hooks/useFontLoadState";
9+
10+
import styles from "./YoutubeHeroSection.module.scss";
11+
12+
export const YoutubeHeroSection = () => {
13+
const titleRef = useRef<HTMLHeadingElement>(null);
14+
const textRef = useRef<HTMLDivElement>(null);
15+
const registerButtonRef = useRef<HTMLButtonElement>(null);
16+
const { fontLoaded } = useFontLoadState();
17+
18+
useGSAP(() => {
19+
if (!fontLoaded) return;
20+
21+
const timeline = gsap.timeline();
22+
timeline.fromTo(titleRef.current, { opacity: 0, y: 50 }, { opacity: 1, y: 0, duration: 2, ease: "expo.out" });
23+
}, [fontLoaded]);
24+
25+
return (
26+
<section className={styles.yt_hero_section__wrapper}>
27+
<h1 className={styles.yt_hero_section__title} ref={titleRef} style={{ opacity: fontLoaded ? 1 : 0 }}>
28+
<span>당신의 수능을 바꿀&nbsp;</span>
29+
<span>마지막 기회</span>
30+
</h1>
31+
32+
<article className={styles.yt_hero_section__video}>
33+
<YouTubeEmbed videoid={"sy0h73_DO5M"} style="margin: 0px auto;" />
34+
</article>
35+
36+
<div ref={textRef} className={styles.yt_hero_section__content} style={{ opacity: fontLoaded ? 1 : 0 }}>
37+
<p>
38+
<span>모의가 아닌 진짜 수능 이용 가이드입니다.</span>
39+
</p>
40+
41+
<Link href="/apply" scroll>
42+
<button ref={registerButtonRef}>
43+
<span>지금 바로 신청하기</span>
44+
<span>
45+
<ChevronRight strokeWidth={1} />
46+
</span>
47+
</button>
48+
</Link>
49+
50+
<footer className={styles.yt_hero_section__footer}>
51+
<ChevronDown size={30} />
52+
</footer>
53+
</div>
54+
</section>
55+
);
56+
};

0 commit comments

Comments
 (0)