Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/pages/Onboarding/SetZipCode.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useEffect, useState } from 'react';

import Spinner from './components/Spinner';

const SetZipCode = ({
Expand All @@ -6,7 +8,13 @@ const SetZipCode = ({
setIsZipCodeSet: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const DUMMY_ZIPCODE = '122A2';
const [isBtnActive, setIsBtnActive] = useState<boolean>(false);

useEffect(() => {
setTimeout(() => {
setIsBtnActive(true);
}, 6300);
}, []);
return (
<>
<header className="flex flex-col items-center">
Expand All @@ -16,13 +24,16 @@ const SetZipCode = ({
</header>
<section className="flex gap-2">
{DUMMY_ZIPCODE.split('').map((char, index) => (
<Spinner key={index} target={`${char}`} index={index}></Spinner>
<Spinner key={index} target={char} index={index}></Spinner>
))}
</section>
<button
type="button"
disabled={!isBtnActive}
className="primary-btn body-m w-full py-2"
onClick={() => setIsZipCodeSet(true)}
onClick={() => {
setIsZipCodeSet(true);
}}
>
다음으로
</button>
Expand Down
121 changes: 77 additions & 44 deletions src/pages/Onboarding/UserInteraction.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
import { useState, useRef, useEffect } from 'react';
import { Link } from 'react-router';
import { twMerge } from 'tailwind-merge';

import envelope from '@/assets/images/closed-letter.png';
import envelopeTop from '@/assets/images/envelope-pink-back-top.png';
// import envelopeTop from '@/assets/images/envelope-pink-back-top.png';
import envelopeFront from '@/assets/images/opened-letter-front.png';

export default function UserInteraction() {
export default function UserInteraction({
setIsAnimationOver,
}: {
setIsAnimationOver: React.Dispatch<React.SetStateAction<boolean>>;
}) {
const imgRef = useRef<HTMLImageElement>(null);
const [imgPos, setImgPos] = useState<{ top: number; width: number }>({ top: 0, width: 0 });
const [imgPos, setImgPos] = useState<{
top: number;
width: number;
height: number;
left: number;
}>({
top: 0,
width: 0,
height: 0,
left: 0,
});
const [imgToBottom, setImgToBottom] = useState<boolean>(false);

const [startAnimation, setStartAnimation] = useState<boolean>(false);
const [openAnimation, setOpenAnimation] = useState<boolean>(false);
// const [openAnimation, setOpenAnimation] = useState<boolean>(false);
const [letterOutAnimation, setLetterOutAnimation] = useState<boolean>(false);
const [envelopeOut, setEnvelopeOut] = useState<boolean>(false);
const [finishAnimation, setFinishAnimation] = useState<boolean>(false);

const handleLetterClick = () => {
if (imgRef.current) {
const rect = imgRef.current.getBoundingClientRect();
setImgPos({ top: rect.top, width: rect.width });
setImgPos({ top: rect.top, width: rect.width, height: rect.height, left: rect.left });
}
setStartAnimation(true);
setTimeout(() => {
Expand All @@ -31,18 +44,18 @@ export default function UserInteraction() {
useEffect(() => {
if (imgToBottom) {
setTimeout(() => {
setOpenAnimation(true);
setLetterOutAnimation(true);
}, 1000);
}
}, [imgToBottom]);

useEffect(() => {
if (openAnimation) {
setTimeout(() => {
setLetterOutAnimation(true);
}, 2000);
}
}, [openAnimation]);
// useEffect(() => {
// if (openAnimation) {
// setTimeout(() => {
// setLetterOutAnimation(true);
// }, 2000);
// }
// }, [openAnimation]);
Comment on lines +52 to +58
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

편지 열리는거 이뻤는데 아쉽군요 ㅠ


useEffect(() => {
if (letterOutAnimation) {
Expand All @@ -56,9 +69,18 @@ export default function UserInteraction() {
if (envelopeOut) {
setTimeout(() => {
setFinishAnimation(true);
}, 2000);
}, 1000);
}
}, [envelopeOut]);

useEffect(() => {
if (finishAnimation) {
setTimeout(() => {
setIsAnimationOver(true);
}, 2000);
}
}, [finishAnimation]);

if (startAnimation === false) {
return (
<>
Expand All @@ -80,67 +102,78 @@ export default function UserInteraction() {
);
} else {
return (
<>
<div className="relative h-[calc(100vh-110px)] w-full overflow-hidden">
<img
src={envelopeFront}
alt="분홍색 편지지"
alt=""
className={twMerge(
`z-30 mx-10 h-auto rounded transition-transform duration-1000 ease-in-out`,
imgToBottom && 'translate-y-full',
`transform-translation z-30 mx-10 h-auto rounded`,
imgToBottom && !envelopeOut && 'animate-envelopeSink',
envelopeOut && 'animate-envelopeOut',
)}
style={{
top: `${imgPos.top}px`,
top: `calc(${imgPos.top}px - 5rem)`,
position: 'absolute',
width: `${imgPos.width}px`,
left: '0px',
}}
/>
{letterOutAnimation && (
<div
className="animate-expandScale to-gray-5 z-20 max-w-[600px] rounded-lg bg-linear-to-b from-white"
style={{
width: `${imgPos.width - imgPos.width * 0.1}px`,
bottom: `${imgPos.top - 0.7 * imgPos.top}px`,
top: `${imgPos.top - 0.5 * imgPos.top}px`,
position: 'absolute',
}}
></div>
)}
{openAnimation && (
{/* {openAnimation && (
<img
src={envelopeTop}
alt=""
className={twMerge(
`z-10 mx-10 h-auto rounded transition-transform duration-1000 ease-in-out`,
imgToBottom && 'translate-y-full',
openAnimation && 'animate-openEnvelope',
`z-10 mx-10 h-auto rounded`,
openAnimation && !envelopeOut && 'animate-openEnvelope',
envelopeOut && 'animate-envelopeOut',
)}
style={{
top: `${imgPos.top}px`,
bottom: `calc(${sinkRefTop.top}px - 7.5rem)`,
position: 'absolute',
width: `${imgPos.width}px`,
transformOrigin: 'bottom',
left: '0px',
}}
/>
)}
)} */}
<img
src={envelope}
alt="분홍색 편지지"
// ref={sinkRef}
// onAnimationEnd={(e) => {
// if (e.animationName === 'envelopeSink') {
// const rect = sinkRef.current?.getBoundingClientRect();
// if (rect?.top) setSinkRefTop({ top: rect?.top });
// console.log('Animation ended. boundingRect top:', rect?.top);
// }
// }}
className={twMerge(
`z-0 mx-10 h-auto rounded transition-transform duration-1000 ease-in-out`,
imgToBottom && 'translate-y-full',
`z-0 mx-10 h-auto rounded`,
imgToBottom && !envelopeOut && 'animate-envelopeSink',
envelopeOut && 'animate-envelopeOut',
)}
style={{
top: `${imgPos.top}px`,
top: `calc(${imgPos.top}px - 5rem)`,
position: 'absolute',
width: `${imgPos.width}px`,
left: '0px',
}}
/>
{/* TODO: 편지지 링크 */}
{finishAnimation && <Link to={'/'}></Link>}
</>
{letterOutAnimation && (
<div
className={twMerge(
'letter-gradient z-20 max-w-[600px] rounded-lg',
finishAnimation ? 'animate-fadeOut' : 'animate-expandScale',
)}
style={{
width: `${imgPos.width - imgPos.width * 0.1}px`,
bottom: `${0.9 * imgPos.height}px`,
top: `${imgPos.top - 0.7 * imgPos.top}px`,
position: 'absolute',
left: `58px`,
}}
></div>
)}
</div>
);
}
}
38 changes: 38 additions & 0 deletions src/pages/Onboarding/WelcomeLetter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useNavigate } from 'react-router';

export default function index() {
// eslint-disable-next-line react-hooks/rules-of-hooks
const navigate = useNavigate();
return (
<main className="animate-fadeIn absolute inset-0 flex h-full w-full flex-col justify-end bg-white px-5 pt-7.5 pb-4 opacity-0">
<article className="basic-theme mt-7.5 mb-9 grow pl-4">
<h1 className="font-malang mt-15">To.따숨이</h1>
<h2 className="font-malang">환영합니다! 우리 함께 마음을 나누어 보아요</h2>
<section className="mt-9" style={{ fontFamily: 'KyoboHandwriting2020A' }}>
<p>안녕하세요, 따숨이님!</p>
<br />
<p>요즘 어떤 말을 하고싶으신가요?</p>
<p>36.5에서 따뜻한 마음의 편지를 나누어 보세요.</p>
<br />
<p>따뜻한 편지 문화를 위해 아래의 안내 사항을 숙지해주세요!</p>
<p>1. 욕설, 비방, 성희롱은 금지입니다.</p>
<p>
2. 만약 위의 이유로 신고를 당할 경우 경고를 받게 되고, 세번의 경고를 받게 되면 서비스를
이용하실 수 없습니다.
</p>
<p>3. 고민 편지에 대한 답장은 검수 후에 전달됩니다.</p>
</section>
<p className="font-malang mt-22">From.9황작물</p>
</article>
<button
className="primary-btn body-sb text-gray-60 h-fit w-full py-2"
onClick={() => {
navigate(`/`);
sessionStorage.removeItem('onBoarding');
}}
>
홈으로 가기
</button>
</main>
);
}
17 changes: 1 addition & 16 deletions src/pages/Onboarding/components/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,9 @@ const Spinner = ({ target, index }: SpinnerProps) => {
const SPEED = 100 + 10 * index;
const [position, setPosition] = useState(0);
const [isRunning, setIsRunning] = useState(true);
let LETTER_HEIGHT = 40;
const LETTER_HEIGHT = 45;
const animationFrameRef = useRef<number | null>(null);

//TODO: 여러 기기에서 실효성 확인
// 웹에서는 없어도 될 것 같음
// calculate full height of the cycle
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (containerRef.current) {
console.log(LETTER_HEIGHT);
const letter = containerRef.current.querySelector('p');
if (letter) {
LETTER_HEIGHT = letter.getBoundingClientRect().height;
}
}
console.log(LETTER_HEIGHT);
}, []);
const FULL_ROTATION = -TARGET_ARR.length * LETTER_HEIGHT;

useEffect(() => {
Expand Down Expand Up @@ -72,7 +58,6 @@ const Spinner = ({ target, index }: SpinnerProps) => {
style={{ willChange: 'transform' }}
>
<div
ref={containerRef}
className="text-center transition-transform duration-500 ease-linear"
style={{ transform: `translateY(${position}px)` }}
>
Expand Down
31 changes: 29 additions & 2 deletions src/pages/Onboarding/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,41 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';

import SetZipCode from './SetZipCode';
import UserInteraction from './UserInteraction';
import WelcomeLetter from './welcomeLetter';

const OnboardingPage = () => {
const [isZipCodeSet, setIsZipCodeSet] = useState<boolean>(false);
const [isAnimationOver, setIsAnimationOver] = useState<boolean>(false);

useEffect(() => {
if (isZipCodeSet || isAnimationOver) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

몬가 isZipCodeSet가 true일때 isAnimationOver도 true일거 같은데 && 말고 || 연산자를 사용하신 이유가 있나용?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 각각이 우편번호 끝나면, 편지 애니메이션 끝나면 true가 되는거라 둘중 하나만 바뀌어도 업데이트를 해야돼서요!

sessionStorage.setItem(
'onBoarding',
JSON.stringify({ isZipCodeSet: isZipCodeSet, isAnimationOver: isAnimationOver }),
);
}
}, [isZipCodeSet, isAnimationOver]);

useEffect(() => {
const prevDataString = sessionStorage.getItem('onBoarding');
if (prevDataString) {
const newData = JSON.parse(prevDataString);
console.log(newData);
setIsZipCodeSet(newData.isZipCodeSet);
setIsAnimationOver(newData.isAnimationOver);
console.log('isZipCode', isZipCodeSet, 'isAnimation', isAnimationOver);
}
}, []);
return (
<main className="inset-0 mx-5 mt-20 mb-[1.875rem] flex grow flex-col items-center justify-between overflow-hidden">
{isZipCodeSet ? <UserInteraction /> : <SetZipCode setIsZipCodeSet={setIsZipCodeSet} />}
{!isZipCodeSet ? (
<SetZipCode setIsZipCodeSet={setIsZipCodeSet} />
) : !isAnimationOver ? (
<UserInteraction setIsAnimationOver={setIsAnimationOver} />
) : (
<WelcomeLetter />
)}
</main>
);
};
Expand Down
Loading