Skip to content

Commit e989600

Browse files
authored
Merge pull request #199 from SOPT-36-NINEDOT/197/refactor/intro
[Refactor] 최초 진입 뷰 리팩토링
2 parents a092bdf + 0ad0340 commit e989600

File tree

15 files changed

+208
-75
lines changed

15 files changed

+208
-75
lines changed

src/App.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
1+
import { Suspense } from 'react';
12
import { RouterProvider } from 'react-router-dom';
23

34
import { router } from '@/route';
5+
import Loading from '@/common/component/Loading/Loading';
46

57
function App() {
6-
return <RouterProvider router={router} />;
8+
return (
9+
<Suspense
10+
fallback={
11+
<Loading
12+
type="goal"
13+
// type="default"
14+
/>
15+
}
16+
>
17+
<RouterProvider router={router} />
18+
</Suspense>
19+
);
720
}
821

922
export default App;

src/page/home/EndSection/EndSection.css.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,5 @@ export const endText = style({
3939
color: colors.white01,
4040
...fonts.display01,
4141
textAlign: 'center',
42+
whiteSpace: 'pre-line',
4243
});

src/page/home/EndSection/EndSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { RefObject } from 'react';
22

33
import * as styles from '@/page/home/EndSection/EndSection.css';
44
import StartButton from '@/page/home/StartButton/StartButton';
5-
import { END_MESSAGE } from '@/page/home/constant/endSection';
5+
import { END_MESSAGE } from '@/page/home/constant/messageConstants';
66
import { fadeSlide } from '@/page/home/style/fadeTransition.css';
77
import type { buttonHandlerType } from '@/page/home/type/buttonHandlerType';
88

@@ -19,7 +19,7 @@ const EndSection = ({ fadeInRef, visible, onClick }: EndSectionProps) => {
1919
<div className={styles.gradientBackground} />
2020
<div ref={fadeInRef} className={`${styles.fadeContainer} ${fadeSlide({ state: fadeState })}`}>
2121
<div className={styles.layoutContainer}>
22-
<h1 className={styles.endText} dangerouslySetInnerHTML={{ __html: END_MESSAGE }} />
22+
<h1 className={styles.endText}>{END_MESSAGE}</h1>
2323
<StartButton onClick={onClick} />
2424
</div>
2525
</div>

src/page/home/Home.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,45 @@
11
import { HomeContainer } from '@/page/home/Home.css';
2-
import { INTRO_MESSAGE } from '@/page/home/constant/scrollSection';
2+
import { INTRO_MESSAGE } from '@/page/home/constant/messageConstants';
33
import { useFadeInOnView } from '@/page/home/hook/useFadeInOnView';
4-
import StartSection from '@/page/home/StartSection/StartSection';
5-
import ScrollSection from '@/page/home/ScrollSection/ScrollSection';
6-
import EndSection from '@/page/home/EndSection/EndSection';
4+
import { StartSection, EndSection } from '@/page/home';
75
import { fadeSlide } from '@/page/home/style/fadeTransition.css';
86
import { useMultipleFadeInOnView } from '@/page/home/hook/useMultipleFadeInOnView';
9-
import mandalAnimation from '@/assets/lottie/mandalart.json';
10-
import aiAnimation from '@/assets/lottie/ai.json';
11-
import todoAnimation from '@/assets/lottie/todo.json';
127
import { useOverlayModal } from '@/common/hook/useOverlayModal';
138
import LoginModal from '@/common/component/LoginModal/LoginModal';
9+
import ScrollSection from '@/page/home/ScrollSection/ScrollSection';
10+
import type { AnimationImporter } from '@/page/home/type/lottieType';
11+
12+
const animationImporters = [
13+
() => import('@/assets/lottie/mandalart.json'),
14+
() => import('@/assets/lottie/ai.json'),
15+
() => import('@/assets/lottie/todo.json'),
16+
] as const satisfies readonly AnimationImporter[];
1417

15-
const animationDataArray = [mandalAnimation, aiAnimation, todoAnimation];
1618
const sectionKeys = ['mandalart', 'ai', 'todo'] as const;
1719

1820
const Home = () => {
1921
const scrolls = useMultipleFadeInOnView();
2022
const end = useFadeInOnView<HTMLDivElement>();
21-
2223
const { openModal, closeModal } = useOverlayModal();
2324

24-
const handleOpenLogin = () => {
25-
openModal(<LoginModal onClose={closeModal} />);
26-
};
25+
const handleOpenLogin = () => openModal(<LoginModal onClose={closeModal} />);
2726

2827
return (
2928
<div className={HomeContainer}>
3029
<StartSection onClick={handleOpenLogin} />
3130

3231
{sectionKeys.map((key, index) => {
3332
const { ref, visible } = scrolls[index];
33+
const direction = index % 2 === 1 ? 'right' : ('left' as const);
34+
3435
return (
3536
<div key={key} ref={ref} className={fadeSlide({ state: visible ? 'in' : 'out' })}>
3637
<ScrollSection
3738
title={INTRO_MESSAGE[key].title}
3839
content={INTRO_MESSAGE[key].content}
39-
index={index}
40-
animationData={animationDataArray[index]}
40+
visible={visible}
41+
direction={direction}
42+
animationImporter={animationImporters[index]}
4143
/>
4244
</div>
4345
);
@@ -47,4 +49,5 @@ const Home = () => {
4749
</div>
4850
);
4951
};
52+
5053
export default Home;

src/page/home/ScrollSection/ScrollSection.css.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { style } from '@vanilla-extract/css';
22
import { recipe } from '@vanilla-extract/recipes';
33

4-
import { colors, fonts } from '@/style/token';
4+
import { colors, fonts, layout } from '@/style/token';
55

66
export const scrollContainer = style({
77
height: 'calc(100vh - 8rem)',
@@ -14,6 +14,12 @@ export const layoutContainer = recipe({
1414
maxWidth: '141.5rem',
1515
margin: '0 auto',
1616
paddingTop: '26.2rem',
17+
18+
'@media': {
19+
'(max-width: 1450px)': {
20+
paddingInline: '4rem',
21+
},
22+
},
1723
},
1824
variants: {
1925
direction: {
@@ -31,19 +37,32 @@ export const titleText = style({
3137
marginBottom: '1.6rem',
3238
color: colors.grey10,
3339
...fonts.display01,
40+
whiteSpace: 'pre-line',
3441
});
3542

3643
export const contentText = style({
3744
color: colors.grey10,
3845
...fonts.subtitle04,
46+
whiteSpace: 'pre-line',
3947
});
4048

41-
export const LottieContainer = style({
42-
display: 'flex',
43-
justifyContent: 'center',
44-
alignItems: 'center',
45-
width: '90rem',
46-
height: '52.6rem',
47-
borderRadius: '30px',
48-
overflow: 'hidden',
49-
});
49+
export const LottieContainer = style([
50+
layout.flexCenter,
51+
{
52+
width: '90rem',
53+
height: '52.6rem',
54+
borderRadius: '30px',
55+
overflow: 'hidden',
56+
},
57+
]);
58+
59+
export const lottieSkeleton = style([
60+
layout.flexCenter,
61+
{
62+
backgroundColor: colors.grey3,
63+
width: '90rem',
64+
height: '52.6rem',
65+
borderRadius: '30px',
66+
overflow: 'hidden',
67+
},
68+
]);

src/page/home/ScrollSection/ScrollSection.tsx

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,69 @@
1+
import { useEffect, useRef, useState } from 'react';
12
import Lottie from 'lottie-react';
3+
import type { LottieRefCurrentProps } from 'lottie-react';
24

35
import * as styles from '@/page/home/ScrollSection/ScrollSection.css';
6+
import type { AnimationData, AnimationImporter } from '@/page/home/type/lottieType';
7+
import { resolveAnimation } from '@/page/home/type/lottieType';
48

59
type ScrollProps = {
610
title: string;
711
content: string;
8-
index: number;
9-
animationData: object;
12+
visible: boolean;
13+
direction: 'left' | 'right';
14+
animationImporter: AnimationImporter;
1015
};
1116

12-
const ScrollSection = ({ title, content, index, animationData }: ScrollProps) => {
13-
const isOdd = index % 2 === 1;
14-
const direction = isOdd ? 'right' : 'left';
17+
const ScrollSection = ({ title, content, visible, direction, animationImporter }: ScrollProps) => {
18+
const [data, setData] = useState<AnimationData | null>(null);
19+
const lottieRef = useRef<LottieRefCurrentProps>(null);
20+
21+
useEffect(() => {
22+
let mounted = true;
23+
24+
if (visible && !data) {
25+
animationImporter().then((mod) => {
26+
if (!mounted) {
27+
return;
28+
}
29+
setData(resolveAnimation(mod));
30+
});
31+
}
32+
33+
if (!visible) {
34+
lottieRef.current?.pause();
35+
} else {
36+
lottieRef.current?.play();
37+
}
38+
39+
return () => {
40+
mounted = false;
41+
};
42+
}, [visible, data, animationImporter]);
1543

1644
return (
1745
<section className={styles.scrollContainer}>
1846
<div className={styles.layoutContainer({ direction })}>
1947
<div>
20-
<h1 className={styles.titleText} dangerouslySetInnerHTML={{ __html: title }} />
21-
<p className={styles.contentText} dangerouslySetInnerHTML={{ __html: content }} />
48+
<h1 className={styles.titleText}>{title}</h1>
49+
<p className={styles.contentText}>{content}</p>
2250
</div>
23-
<Lottie className={styles.LottieContainer} animationData={animationData} loop={true} />
51+
52+
{data ? (
53+
<Lottie
54+
className={styles.LottieContainer}
55+
lottieRef={lottieRef}
56+
animationData={data}
57+
loop
58+
autoplay={false}
59+
rendererSettings={{
60+
progressiveLoad: true,
61+
preserveAspectRatio: 'xMidYMid meet',
62+
}}
63+
/>
64+
) : (
65+
<div className={styles.lottieSkeleton} aria-hidden />
66+
)}
2467
</div>
2568
</section>
2669
);

src/page/home/StartSection/StartSection.css.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const gradientBlue = style({
1717
height: '105.7rem',
1818
borderRadius: '105.7px',
1919
background: createRadialGradient('50, 95, 236', 0.3),
20+
pointerEvents: 'none',
2021
});
2122

2223
export const gradientGreen = style({
@@ -28,34 +29,43 @@ export const gradientGreen = style({
2829
borderRadius: '71.1px',
2930
background: createRadialGradient('59, 255, 160', 0.7),
3031
opacity: 0.2,
32+
pointerEvents: 'none',
3133
});
3234

3335
export const vectorLine = style({
36+
backgroundImage: `url(/src/assets/image/vector-line.svg)`,
37+
backgroundRepeat: 'no-repeat',
38+
backgroundSize: 'cover',
39+
backgroundPosition: 'center',
3440
position: 'absolute',
35-
top: '3rem',
36-
right: '-12rem',
37-
width: '100%',
38-
height: '100%',
41+
inset: 0,
3942
pointerEvents: 'none',
4043
});
4144

4245
export const layoutContainer = style({
43-
maxWidth: '141.5rem',
46+
maxWidth: '141.6rem',
4447
margin: '0 auto',
4548
paddingTop: '21.8rem',
46-
paddingBottom: '30rem',
49+
50+
'@media': {
51+
'(max-width: 1450px)': {
52+
paddingInline: '4rem',
53+
},
54+
},
4755
});
4856

4957
export const titleText = style({
5058
marginBottom: '2.6rem',
5159
color: colors.grey10,
5260
...fonts.display04,
61+
whiteSpace: 'pre-line',
5362
});
5463

5564
export const contentText = style({
5665
marginBottom: '6.8rem',
5766
color: colors.grey10,
5867
...fonts.title02,
68+
whiteSpace: 'pre-line',
5969
});
6070

6171
const scrollBounce = keyframes({

src/page/home/StartSection/StartSection.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as styles from '@/page/home/StartSection/StartSection.css';
2-
import { CONTENT_MESSAGE, TITLE_MESSAGE } from '@/page/home/constant/startSection';
2+
import { CONTENT_MESSAGE, TITLE_MESSAGE } from '@/page/home/constant/messageConstants';
33
import StartButton from '@/page/home/StartButton/StartButton';
4-
import VectorLine from '@/assets/image/vector-line.svg';
54
import { IcUnder } from '@/assets/svg';
65
import type { buttonHandlerType } from '@/page/home/type/buttonHandlerType';
76

@@ -10,10 +9,10 @@ const StartSection = ({ onClick }: buttonHandlerType) => {
109
<section className={styles.startContainer}>
1110
<div className={styles.gradientBlue} />
1211
<div className={styles.gradientGreen} />
13-
<img src={VectorLine} alt="벡터 라인" className={styles.vectorLine} />
12+
<div className={styles.vectorLine} aria-hidden />
1413
<div className={styles.layoutContainer}>
15-
<h1 className={styles.titleText} dangerouslySetInnerHTML={{ __html: TITLE_MESSAGE }} />
16-
<p className={styles.contentText} dangerouslySetInnerHTML={{ __html: CONTENT_MESSAGE }} />
14+
<h1 className={styles.titleText}>{TITLE_MESSAGE}</h1>
15+
<p className={styles.contentText}>{CONTENT_MESSAGE}</p>
1716
<StartButton onClick={onClick} />
1817
<div className={styles.scrollContainer}>
1918
<IcUnder className={styles.scrollIcon} />

src/page/home/constant/endSection.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export const TITLE_MESSAGE = '목표를 세운 순간부터\n시작된 변화';
2+
export const CONTENT_MESSAGE =
3+
'계획만으로 끝나지 않도록,\n실천까지 함께하는 만다라트 루틴을 경험해보세요.';
4+
5+
export const INTRO_MESSAGE = {
6+
mandalart: {
7+
title: `목표 달성을 위해\n할 일을 세분화하여,`,
8+
content: `구체적인 실행 계획을 제공해\n빠르게 목표를 달성할 수 있도록 도와줄게요.`,
9+
},
10+
ai: {
11+
title: `AI의 도움으로\n부담감을 덜어주도록,`,
12+
content: `무엇을 해야할지 모르겠다면\nAI의 도움을 받아보세요.`,
13+
},
14+
todo: {
15+
title: `지금 해야할 일을\n분명히 보여주는,`,
16+
content: `무엇을 해야 할지 헷갈릴 필요 없이,\n오늘 실천할 한 가지에 집중해보세요.`,
17+
},
18+
} as const;
19+
20+
export const END_MESSAGE = `지금 바로\nNiNEDOT와 함께 하세요!`;

0 commit comments

Comments
 (0)