-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SP2] 메인 페이지 SOPT 소개 애니메이션 작업 (#287)
* 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
Showing
13 changed files
with
407 additions
and
3 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
src/views/MainPage/components/IntroSection/IntroContent/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
231
src/views/MainPage/components/IntroSection/IntroContent/style.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
`; |
Oops, something went wrong.