Skip to content

Commit

Permalink
[SP2] 메인 페이지 SOPT 소개 애니메이션 작업 (#287)
Browse files Browse the repository at this point in the history
* chore: framer-motion 설치

* assets: 필요한 아이콘 및 이미지 저장

* feat: SOPT 소개 문구 상수화

* feat: 소개 카드 컴포넌트 구현

* feat: 소개 카드 목록 컴포넌트 구현

* style: 스타일 수정

* asset: 사진 해상도 변경

* chore: 네이밍 및 스타일 자잘한 수정

* chore: 불필요한 lock 파일 제거 및 업데이트

* style: 태블릿, 모바일 반응형

* style: 라인, 백라이트 코드 수정

* style: 화면 사이즈에 따라 너비 조정

* style: 요소들 z-index 조정

* style: 요소 너비 화면 사이즈에 따라 조정

* style: 백라이트 코드 변경

* style: svg 코드 수정

* style: 백라이트 하단 잘림 수정

* chore: 불필요한 파일 제거

* chore: 사용 안하는 라이브러리 제거

* chore: 불필요한 의존성 제거

* style: 라인 길이 수정

* style: 소개 섹션 최상단, 최하단 그림자 추가

---------

Co-authored-by: solar3070 <>
  • Loading branch information
solar3070 authored Nov 22, 2023
1 parent 1d7970d commit f3872a3
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 3 deletions.
2 changes: 0 additions & 2 deletions package-lock.json

This file was deleted.

15 changes: 15 additions & 0 deletions src/assets/icons/ic_ellipse_blue.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/img_intro_card1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/img_intro_card2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/img_intro_card3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/Footer/MakersNForm/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const FooterForm = styled.div<{ hide: boolean }>`
background-color: #1c1d1e;
transition: transform 0.3s;
z-index: 10;
z-index: 99;
${({ hide }) =>
hide
Expand Down
30 changes: 30 additions & 0 deletions src/lib/constants/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { default as ImgIntroCard1 } from '@src/assets/images/img_intro_card1.png';
import { default as ImgIntroCard2 } from '@src/assets/images/img_intro_card2.png';
import { default as ImgIntroCard3 } from '@src/assets/images/img_intro_card3.png';

export const INTRO_CONTENT_LIST = [
{
id: 1,
title: '열정이 이끄는\n최고 수준의 몰입',
detail:
'열정 하나로 뭉친 SOPT는 끊임 없이 집중하며 성장합니다.\n언제나 어떤 일에 대해서든 최고 수준의 몰입을 유지합니다.',
src: ImgIntroCard1.src,
},
{
id: 2,
title: '가진 것은 무엇이든\n나누는 문화',
detail:
'SOPT에서는 모두가 자신의 지식과 경험을\n적극적으로 나눕니다. 이를 통해 다양한 관점에서\n세상의 문제를 해결할 수 있습니다.',
src: ImgIntroCard2.src,
},
{
id: 3,
title: '함께이기 때문에\n가능한 도전',
detail:
'200여 명의 활동 회원, 3000여 명의 명예 회원들과 함께이기에\n그 어떤 목표에도 용기 내어 도전할 수 있습니다.',
src: ImgIntroCard3.src,
},
];

export const FIRST_INTRO_CONTENT = 1;
export const LAST_INTRO_CONTENT = 3;
6 changes: 6 additions & 0 deletions src/lib/types/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type IntroContentType = {
id: number;
title: string;
detail: string;
src: string;
};
2 changes: 2 additions & 0 deletions src/views/MainPage/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import DummyDiv from '@src/components/common/DummyDiv';
import PageLayout from '@src/components/common/PageLayout';
import IntroSection from '@src/views/MainPage/components/IntroSection';
import Banner from './components/Banner';
import Introduce from './components/Introduce';
import ScrollInteractiveLogo from './components/ScrollInteractiveLogo';
Expand All @@ -9,6 +10,7 @@ function MainPage() {
<PageLayout>
<Banner />
<Introduce />
<IntroSection />
<ScrollInteractiveLogo />
<DummyDiv height="400vh" backgroundColor="white" />
</PageLayout>
Expand Down
82 changes: 82 additions & 0 deletions src/views/MainPage/components/IntroSection/IntroContent/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { motion } from 'framer-motion';
import { useState } from 'react';
import { ReactComponent as IcEllipseBlue } from '@src/assets/icons/ic_ellipse_blue.svg';
import { FIRST_INTRO_CONTENT, LAST_INTRO_CONTENT } from '@src/lib/constants/main';
import { IntroContentType } from '@src/lib/types/main';
import * as S from './style';

interface IntroContentProps {
content: IntroContentType;
}

export default function IntroContent({ content }: IntroContentProps) {
const contentDraw = {
initial: { opacity: 0 },
visible: { opacity: 1, transition: { opacity: { delay: 0.2, duration: 0.7 } } },
};

const upMotion = {
initial: { y: 10 },
visible: {
y: 0,
transition: { type: 'spring', damping: 20, stiffness: 120, y: { delay: 0.3, duration: 0.8 } },
},
};

const lineDraw = {
initial: { pathLength: 0, opacity: 0 },
visible: {
pathLength: 1,
opacity: 1,
transition: {
pathLength: { delay: 0.8, type: 'spring', duration: 3, bounce: 0, ease: 'easeInOut' },
opacity: { delay: 0.8, duration: 0.4 },
},
},
};

const [isContentVisible, setIsContentVisible] = useState(false);

return (
<S.IntroWrapper>
{content.id === FIRST_INTRO_CONTENT && <S.Header />}
<S.Intro>
{content.id !== LAST_INTRO_CONTENT && (
<S.AnimatedLine initial="initial" whileInView="visible" viewport={{ amount: 0.2 }}>
<motion.line x1="10" y1="10" x2="10" y2="100%" stroke="#d7f5ff" variants={lineDraw} />
</S.AnimatedLine>
)}
<motion.div
initial="initial"
whileInView="visible"
variants={contentDraw}
viewport={{ amount: 0.5 }}
onViewportEnter={() => setIsContentVisible(true)}
onViewportLeave={() => setIsContentVisible(false)}
>
<S.Circle>
<IcEllipseBlue />
</S.Circle>
<S.Content
initial="initial"
whileInView="visible"
variants={upMotion}
viewport={{ amount: 0.2 }}
>
<S.ContentTitle>{content.title}</S.ContentTitle>
<S.ContentDetail>{content.detail}</S.ContentDetail>
<S.ContentImage
src={content.src}
width={707}
height={471}
loading="lazy"
alt="SOPT 소개 이미지"
/>
</S.Content>
</motion.div>
</S.Intro>
<S.BackLight isContentVisible={isContentVisible} />
{content.id === LAST_INTRO_CONTENT && <S.Footer />}
</S.IntroWrapper>
);
}
231 changes: 231 additions & 0 deletions src/views/MainPage/components/IntroSection/IntroContent/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import styled from '@emotion/styled';
import { motion } from 'framer-motion';
import Image from 'next/image';

export const IntroWrapper = styled(motion.div)`
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 100vw;
height: 100vh;
`;

export const Shadow = styled.div`
position: absolute;
z-index: 90;
width: 100%;
height: 120px;
`;

export const Header = styled(Shadow)`
top: 0;
background: linear-gradient(180deg, #0f1012 0%, rgba(15, 16, 16, 0) 100%);
`;

export const Footer = styled(Shadow)`
bottom: 0;
background: linear-gradient(360deg, #0f1012 0%, rgba(15, 16, 16, 0) 100%);
`;

export const Intro = styled.div`
position: relative;
padding: 0 60px 0 104px;
@media (max-width: 768px) {
padding: 0 30px;
}
`;

export const AnimatedLine = styled(motion.svg)`
position: absolute;
top: 46px;
left: 57px;
z-index: -1;
width: 20px;
height: calc(100vh + 7px);
stroke-width: 4px;
stroke-linecap: round;
@media (max-width: 768px) {
top: 285px;
left: 42px;
}
`;

export const Circle = styled(motion.div)`
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: -160px;
left: -300.91px;
z-index: 10;
width: 735.833px;
height: 430px;
transform: rotate(-21.134deg);
flex-shrink: 0;
border-radius: 735.833px;
background: radial-gradient(
50% 50% at 50% 50%,
rgba(46, 57, 60, 0.35) 0%,
rgba(7, 9, 12, 0) 100%
);
& > svg {
width: 130px;
height: 130px;
}
@media (max-width: 1440px) and (min-width: 769px) {
grid-template-rows: auto 1fr;
grid-gap: 24px 0px;
top: -170px;
left: -300.91px;
}
@media (max-width: 768px) {
top: 80.19px;
left: -315.83px;
& > svg {
width: 100px;
height: 100px;
}
}
`;

export const Content = styled(motion.div)`
display: grid;
grid-template-areas:
'title image'
'desc image';
grid-template-rows: auto 1fr;
grid-template-columns: 590px minmax(323px, 1fr);
grid-gap: 24px 57px;
position: relative;
z-index: 20;
@media (max-width: 1440px) and (min-width: 769px) {
grid-template-columns: minmax(400px, 1fr) minmax(auto, 1fr);
grid-gap: 24px 36px;
}
@media (max-width: 768px) {
grid-template-areas:
'image'
'title'
'desc';
grid-template-rows: auto auto 103px;
grid-template-columns: none;
grid-gap: 16px;
font-size: 28px;
}
`;

export const ContentTitle = styled(motion.h2)`
grid-area: title;
margin-top: 38px;
background: linear-gradient(93deg, #e1edf0 78.65%, #fff 128.82%, #e6eff2 137.19%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-family: SUIT;
font-size: 48px;
font-style: normal;
font-weight: 700;
line-height: normal;
white-space: pre-wrap;
@media (max-width: 1440px) and (min-width: 769px) {
margin-top: 28px;
font-size: 40px;
}
@media (max-width: 768px) {
width: fit-content;
margin-top: 48px;
margin-left: 58px;
font-size: 28px;
}
`;

export const ContentDetail = styled(motion.h3)`
grid-area: desc;
color: #b9c7c8;
font-family: SUIT;
font-size: 24px;
font-style: normal;
font-weight: 500;
line-height: 160%; /* 38.4px */
letter-spacing: -0.48px;
white-space: pre-wrap;
@media (max-width: 1440px) and (min-width: 769px) {
font-size: 20px;
letter-spacing: -0.4px;
word-break: keep-all;
}
@media (max-width: 1091px) and (min-width: 769px) {
white-space: normal;
}
@media (max-width: 768px) {
max-width: 259px;
margin-left: 58px;
font-size: 16px;
letter-spacing: -0.32px;
white-space: normal;
word-break: keep-all;
}
`;

export const ContentImage = styled(Image)`
grid-area: image;
width: 100%;
height: auto;
border-radius: 50px;
object-fit: cover;
@media (max-width: 1440px) and (min-width: 769px) {
max-width: 540px;
min-height: 270px;
border-radius: 36px;
}
@media (max-width: 768px) {
max-width: 323px;
min-width: 264px;
height: 215px;
border-radius: 25px;
box-shadow: 0px 0px 64px 0px rgba(15, 16, 18, 0.9);
}
`;

export const BackLight = styled.div<{ isContentVisible: boolean }>`
position: absolute;
top: -114.94px;
left: 385.37px;
z-index: -99;
width: 1954px;
height: 1084px;
transform: rotate(7.639deg);
background: radial-gradient(45.16% 45.16% at 50% 50%, #2c3242 0%, rgba(15, 15, 18, 0) 100%);
opacity: ${({ isContentVisible }) => (isContentVisible ? '1' : '0')};
transition: opacity 0.3s ease-in-out;
@media (max-width: 768px) {
opacity: 0;
}
`;
Loading

0 comments on commit f3872a3

Please sign in to comment.