Skip to content

Commit

Permalink
feat: 랜딩페이지 제작 (#132)
Browse files Browse the repository at this point in the history
* add: 랜딩페이지에 사용되는 이미지 파일들 추가

* feat: 랜딩페이지 파일 추가 및 2번째 섹션까지 작업

* feat: 약속시간 만들기 부분 추가

* feat: 시간 입력하기 부분 추가 및 배치 수정

* feat: 우선순위 정하기 부분 추가

* add: 랜딩페이지 배경 추가

* fix: 컴포넌트 구조 변경

* feat: LastWrapper 추가

* style:LastWrapper 스타일 수정

* feat: Components 공통 컴포넌트로 구현

* feat: 스크롤 애니메이션을 위한 hook 파일 추가 및 적용

* feat: 스크롤 해보세요에 적용할 플로팅 애니메이션 추가

* chore: 불필요헌 코드 삭제

* fix: 배경이미지 및 배치 수정

* chore: Start 페이지 파일 삭제

* fix: 파일명 index로 수정

* fix: 토글 기본값 하나씩으로 수정

* chore: 이미지 파일명 수정

* style: S dot 네이밍 방식 도입

* chore: 오탈자 이미지 수정
  • Loading branch information
YesHyeon authored Sep 4, 2023
1 parent cf344b3 commit e50c70d
Show file tree
Hide file tree
Showing 20 changed files with 357 additions and 57 deletions.
5 changes: 3 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import AddTime from './pages/addTime';
import Current from './pages/current';
import RoomCalendar from './pages/roomCalendar';
import RoomStart from './pages/roomStart';
import Start from './pages/start';
import Timer from './pages/roomTimer';
import Login from './pages/login';
import Result from './pages/result';
Expand All @@ -14,13 +13,15 @@ import Error from './pages/404';
import useScrollToTop from './hooks/useScrollToTop';
import useGoogleAnalytics from './hooks/useGoogleAnalytics';

import Landing from './pages/landing';

function App() {
useGoogleAnalytics();
useScrollToTop();

return (
<Routes>
<Route path="/" element={<Start />} />
<Route path="/" element={<Landing />} />
<Route path={`${ROUTES.ROOM_START}`} element={<RoomStart />} />
<Route path={`${ROUTES.ROOM_CALENDAR}`} element={<RoomCalendar />} />
<Route path={`${ROUTES.ROOM_TIMER}`} element={<Timer />} />
Expand Down
Binary file added src/assets/icons/scrollArrow.webp
Binary file not shown.
Binary file added src/assets/images/landing1.webp
Binary file not shown.
Binary file added src/assets/images/landing2.webp
Binary file not shown.
Binary file added src/assets/images/landing3.webp
Binary file not shown.
Binary file added src/assets/images/landing4.webp
Binary file not shown.
Binary file added src/assets/images/landing5.webp
Binary file not shown.
Binary file added src/assets/images/landing6.webp
Binary file not shown.
Binary file added src/assets/images/landingBack.webp
Binary file not shown.
Binary file added src/assets/images/logo.webp
Binary file not shown.
Binary file added src/assets/images/rabbit.webp
Binary file not shown.
2 changes: 1 addition & 1 deletion src/components/createRoom/calendar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface Calendar {
}

const Calendar = ({ dates, setDates, setMonth }: Calendar) => {
const [isRange, setIsRange] = useState<boolean>(true);
const [isRange, setIsRange] = useState<boolean>(false);
const [value, setValue] = useState();

const ko = {
Expand Down
4 changes: 2 additions & 2 deletions src/components/createRoom/toggle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ const Toggle = ({ text, toggle, setData }: ToggleProps) => {

return (
<ToggleBtn onClick={clickedToggle} toggle={isToggle}>
<ToggleText>{text[0]}</ToggleText>
<ToggleText>{text[1]}</ToggleText>
<Circle toggle={isToggle}>{isToggle ? text[0] : text[1]}</Circle>
<ToggleText>{text[0]}</ToggleText>
<Circle toggle={!isToggle}>{isToggle ? text[0] : text[1]}</Circle>
</ToggleBtn>
);
};
Expand Down
26 changes: 26 additions & 0 deletions src/hooks/useComponentOnScreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect, RefObject } from 'react';

export const useComponentOnScreen = (refs: RefObject<HTMLDivElement>[]) => {
const handleScroll = (entries: any[]) => {
entries.forEach((item) => {
if (item.isIntersecting) {
item.target.style.opacity = 1;
item.target.style.transform = 'translateZ(0)';
}
});
};

const observer = new IntersectionObserver(handleScroll, {
threshold: 0.2,
});

useEffect(() => {
refs.forEach((ref) => {
if (ref.current) {
observer.observe(ref.current);
}
});

return () => observer && observer.disconnect();
}, []);
};
188 changes: 188 additions & 0 deletions src/pages/landing/index.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import styled from '@emotion/styled';
import theme from '../../styles/theme';
import landingBack from '@/assets/images/landingBack.webp';
import { flotingAnimation } from '@/utils/flotingAnimation';

export const MainContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
max-width: 412px;
height: 6000px;
margin: 0 auto;
background-image: url(${landingBack});
background-size: cover;
background-position: center;
background-color: ${theme.colors.purple05};
&::-webkit-scrollbar {
display: none;
}
overflow-x: hidden;
`;

export const StartWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
padding-top: 150px;
@media screen and (max-width: 375px) {
padding-top: 80px;
}
height: 900px;
.logo-header {
${theme.typography.semibold02}
background: linear-gradient(180deg, #e3e7ff 0%, #f8f8ff 100%);
color: transparent;
-webkit-background-clip: text;
padding-bottom: 12px;
}
.logo {
width: 250px;
height: 42px;
margin-bottom: 58px;
}
.rabbit {
width: 250px;
}
`;

export const ScrollWrapper = styled.div`
display: flex;
flex-direction: row;
padding-top: 55px;
gap: 6px;
position: absolute;
bottom: 100px;
color: ${theme.colors.gray01};
${theme.typography.semibold04}
.arrow {
width: 17px;
height: 19px;
}
animation: ${flotingAnimation} 2s 5;
`;

export const IntroWrapper = styled.div`
display: flex;
flex-direction: column;
width: 100%;
height: 550px;
font-size: 22px;
color: ${theme.colors.gray01};
opacity: 0;
transition: all 2s;
transform: translateY(150px);
align-items: center;
${theme.typography.semibold04}
gap: 40px;
white-space: pre-line;
.title {
text-align: center;
color: ${theme.colors.gray01};
${theme.typography.semibold04};
font-size: 22px;
}
.chat {
width: calc(100% - 42px);
}
.logo {
width: 199px;
margin-inline: 10px;
}
.section {
margin-top: 50px;
display: flex;
flex-direction: column;
align-items: center;
.section-text {
font-size: 28px;
}
}
`;

export const ContentsWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 70px;
padding-top: 340px;
@media screen and (max-width: 375px) {
gap: 170px;
}
white-space: pre-line;
`;

export const ContentWrapper = styled.div<{ index: number }>`
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
font-size: 22px;
opacity: 0;
transition: all 2s;
transform: translateY(150px);
img {
width: ${(props) =>
props.index == 1
? 'calc(100% - 142px)'
: props.index == 2
? 'calc(100% - 41px)'
: props.index == 3
? 'calc(100% - 140px)'
: 'calc(100% - 144px)'};
}
`;

export const LastWrapper = styled.div`
margin-top: 180px;
white-space: pre-line;
text-align: center;
opacity: 0;
transition: all 2s;
transform: translateY(50px);
.title {
color: ${theme.colors.gray01};
${theme.typography.semibold02}
}
`;

export const TitleWrapper = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
text-align: center;
white-space: pre-line;
padding-bottom: 45px;
.title-header {
color: ${theme.colors.purple06};
${theme.typography.semibold06};
}
.title {
color: ${theme.colors.gray07};
${theme.typography.semibold01};
}
`;
123 changes: 123 additions & 0 deletions src/pages/landing/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { useRef } from 'react';
import { Link } from 'react-router-dom';

import * as S from './index.styles';

import BottomButton from '@/components/commons/bottomButton';
import { useComponentOnScreen } from '@/hooks/useComponentOnScreen';

import Rabbit from '@/assets/images/rabbit.webp';
import Logo from '@/assets/images/logo.webp';
import ScrollArrow from '@/assets/icons/scrollArrow.webp';
import landing1 from '@/assets/images/landing1.webp';
import landing2 from '@/assets/images/landing2.webp';
import landing3 from '@/assets/images/landing3.webp';
import landing4 from '@/assets/images/landing4.webp';
import landing5 from '@/assets/images/landing5.webp';
import landing6 from '@/assets/images/landing6.webp';

const Landing = () => {
const introRef = useRef<HTMLDivElement>(null);
const firstRef = useRef<HTMLDivElement>(null);
const secondRef = useRef<HTMLDivElement>(null);
const thirdRef = useRef<HTMLDivElement>(null);
const fourthRef = useRef<HTMLDivElement>(null);
const lastRef = useRef<HTMLDivElement>(null);

useComponentOnScreen([
introRef,
firstRef,
secondRef,
thirdRef,
fourthRef,
lastRef,
]);

const CONTENTS = [
{
id: 1,
titleHeader: '약속 시간 만들기',
title: '간단하게 약속 모임을\n만들어보세요',
images: [landing2, landing3],
ref: firstRef,
},
{
id: 2,
titleHeader: '시간 입력하기',
title: '되는 시간/안되는 시간 토글로\n일정을 등록해보세요',
images: [landing4],
ref: secondRef,
},
{
id: 3,
titleHeader: '실시간 확인하기',
title: '일정등록 타이머와 함께\n실시간 참여율을 확인할 수 있어요',
images: [landing5],
ref: thirdRef,
},
{
id: 4,
titleHeader: '우선순위 확인하기',
title: '조율 결과를\n한눈에 확인해볼까요?',
images: [landing6],
ref: fourthRef,
},
];

return (
<S.MainContainer>
<S.StartWrapper>
<div className="logo-header">쉽고 빠른 약속 정하기</div>
<img className="logo" src={Logo} />
<img className="rabbit" src={Rabbit} />
<S.ScrollWrapper>
<img className="arrow" src={ScrollArrow} />
스크롤해보세요
</S.ScrollWrapper>
</S.StartWrapper>
<S.IntroWrapper ref={introRef}>
<div className="title">
{`3인 이상 약속을 잡을 때,
일정 조율하기 어렵지 않으셨나요?`}
</div>
<img className="chat" src={landing1} />
<div className="section">
<div className="section-logo">
<img className="logo" src={Logo} />
</div>
<div className="section-text">도와드릴게요!</div>
</div>
</S.IntroWrapper>
<S.ContentsWrapper>
{CONTENTS.map((item) => {
return (
<S.ContentWrapper index={item.id} key={item.id} ref={item.ref}>
<S.TitleWrapper>
<div className="title-header">{item.titleHeader}</div>
<div className="title">{item.title}</div>
</S.TitleWrapper>
{item.images.map((image) => {
return <img src={image} key={image} />;
})}
</S.ContentWrapper>
);
})}
</S.ContentsWrapper>
<S.LastWrapper ref={lastRef}>
<div className="title">
{`간편하고 빠르게 약속시간을 정하고 싶다면
모두의 시간과 함께 해보세요!`}
</div>
</S.LastWrapper>
<Link to="/roomStart">
<BottomButton
text="시작하기"
isBackgroundVisible={false}
isActivated={true}
/>
</Link>
</S.MainContainer>
);
};

export default Landing;
2 changes: 1 addition & 1 deletion src/pages/roomStart/index.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ export const ChceckContainer = styled.div`
padding-right: 20px;
`;

export const CheckListText = styled.text`
export const CheckListText = styled.div`
font-family: Pretendard;
`;

Expand Down
Loading

0 comments on commit e50c70d

Please sign in to comment.