Skip to content

Commit e453830

Browse files
authored
Merge pull request #43 from happbob/feat/OD-7
Refactor: transition/animation 속성 적절히 배치
2 parents eaab5ce + 69bda79 commit e453830

5 files changed

Lines changed: 51 additions & 92 deletions

File tree

src/components/BottomSheet/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ const BottomSheet: React.FC<BottomSheetProps> = ({
1111
}) => {
1212
const startY = useRef<number | null>(null);
1313
const [initialRender, setInitialRender] = useState(true);
14+
const [isRendered, setIsRendered] = useState(false);
1415
const [currentTranslateY, setCurrentTranslateY] = useState(0);
1516
const [isDragging, setIsDragging] = useState<boolean>(false);
1617

1718
useEffect(() => {
1819
if (isOpenBottomSheet) {
1920
setInitialRender(false);
21+
setIsRendered(true);
2022
setCurrentTranslateY(0); // 초기화
23+
} else {
24+
setIsRendered(false);
2125
}
2226
}, [isOpenBottomSheet]);
2327

@@ -105,7 +109,7 @@ const BottomSheet: React.FC<BottomSheetProps> = ({
105109

106110
return (
107111
<BottomSheetWrapper
108-
$isOpenBottomSheet={isOpenBottomSheet}
112+
$isOpenBottomSheet={isRendered}
109113
onClick={(e: React.MouseEvent) => {
110114
// BottomSheet 외부를 클릭할 경우 BottomSheet 닫음
111115
if (!isDragging && e.target === e.currentTarget) {
@@ -117,7 +121,7 @@ const BottomSheet: React.FC<BottomSheetProps> = ({
117121
onPointerDown={onPointerDown}
118122
onTouchStart={onPointerDown}
119123
$currentTranslateY={currentTranslateY}
120-
$isOpenBottomSheet={isOpenBottomSheet}
124+
$isOpenBottomSheet={isRendered}
121125
$isHandlerVisible={isHandlerVisible}
122126
>
123127
{isHandlerVisible && <Handler />}
Lines changed: 20 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import styled, { keyframes, css } from 'styled-components';
1+
import React from 'react';
2+
import styled from 'styled-components';
23

34
export const BottomSheetWrapper = styled.div<{ $isOpenBottomSheet: boolean }>`
45
position: fixed;
@@ -8,21 +9,22 @@ export const BottomSheetWrapper = styled.div<{ $isOpenBottomSheet: boolean }>`
89
height: 100%;
910
z-index: 999;
1011
background-color: rgba(0, 0, 0, 0.3);
11-
animation: ${(props) =>
12-
props.$isOpenBottomSheet
13-
? css`
14-
${fadeIn} 0.2s ease-out forwards
15-
`
16-
: css`
17-
${fadeOut} 0.2s ease-out forwards
18-
`};
12+
opacity: ${({ $isOpenBottomSheet }) => ($isOpenBottomSheet ? 1 : 0)};
13+
visibility: ${({ $isOpenBottomSheet }) => ($isOpenBottomSheet ? 'visible' : 'hidden')};
14+
transition:
15+
opacity 0.2s ease-out,
16+
visibility 0.3s ease-out;
1917
`;
2018

21-
export const BottomSheetLayout = styled.div.attrs<{ $currentTranslateY: number }>(({ $currentTranslateY }) => ({
22-
style: {
23-
transform: `translate(-50%, ${$currentTranslateY}px)`,
24-
},
25-
}))<{ $isOpenBottomSheet: boolean; $currentTranslateY: number; $isHandlerVisible: boolean }>`
19+
export const BottomSheetLayout = styled.div.attrs<{ $currentTranslateY: number; $isOpenBottomSheet: boolean }>(
20+
({ $currentTranslateY, $isOpenBottomSheet }) => ({
21+
style: {
22+
transform: `translate(-50%, ${$isOpenBottomSheet ? `${$currentTranslateY}px` : '100%'})`,
23+
},
24+
}),
25+
)<{
26+
$isHandlerVisible: boolean;
27+
}>`
2628
position: fixed;
2729
bottom: 0;
2830
flex-direction: column;
@@ -31,21 +33,14 @@ export const BottomSheetLayout = styled.div.attrs<{ $currentTranslateY: number }
3133
left: 50%;
3234
border-radius: 0.938rem 0.938rem 0 0;
3335
background-color: ${({ theme }) => theme.colors.white};
34-
padding-top: ${(props) => (props.$isHandlerVisible ? '1.3rem' : '0.9375rem')};
36+
padding-top: ${({ $isHandlerVisible }) => ($isHandlerVisible ? '1.3rem' : '0.9375rem')};
3537
z-index: 200;
3638
user-select: none;
3739
touch-action: none;
38-
animation: ${(props) =>
39-
props.$isOpenBottomSheet
40-
? css`
41-
${slideUp} 0.2s ease-out
42-
`
43-
: css`
44-
${slideDown} 0.2s ease-out forwards
45-
`};
40+
transition: transform 0.3s;
4641
`;
4742

48-
export const Handler = styled.hr`
43+
export const Handler = React.memo(styled.hr`
4944
width: 3rem;
5045
margin: 0 auto;
5146
height: 0.25rem;
@@ -54,40 +49,4 @@ export const Handler = styled.hr`
5449
border-radius: 0.125rem;
5550
z-index: 300;
5651
cursor: pointer;
57-
`;
58-
59-
const fadeIn = keyframes`
60-
from {
61-
opacity: 0;
62-
}
63-
to {
64-
opacity: 1;
65-
}
66-
`;
67-
68-
const fadeOut = keyframes`
69-
from {
70-
opacity: 1;
71-
visibility: visible;
72-
}
73-
to {
74-
opacity: 0;
75-
visibility: hidden;
76-
}
77-
`;
78-
79-
const slideUp = keyframes`
80-
from {
81-
transform: translate(-50%, 100%);
82-
}
83-
to {
84-
transform: translate(-50%, 0);
85-
}
86-
`;
87-
88-
const slideDown = keyframes`
89-
to {
90-
transform: translate(-50%, 100%);
91-
visibility: hidden;
92-
}
93-
`;
52+
`);

src/components/BottomSheetMenu/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { StyledText } from '../Text/StyledText';
22
import theme from '../../styles/theme';
33
import { BottomSheetMenuLayout, SheetItem, Icon } from './styles';
44
import { BottomSheetMenuProps, SheetItemDto } from './dto';
5+
import React from 'react';
56

6-
const BottomSheetMenu: React.FC<BottomSheetMenuProps> = ({ items, marginBottom }) => {
7+
const BottomSheetMenu: React.FC<BottomSheetMenuProps> = React.memo(({ items, marginBottom }) => {
78
return (
89
<BottomSheetMenuLayout $marginBottom={marginBottom}>
910
{items.map((item: SheetItemDto, index) => (
@@ -19,6 +20,6 @@ const BottomSheetMenu: React.FC<BottomSheetMenuProps> = ({ items, marginBottom }
1920
))}
2021
</BottomSheetMenuLayout>
2122
);
22-
};
23+
});
2324

2425
export default BottomSheetMenu;

src/components/Loading/index.tsx

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,12 @@
1-
import { useEffect, useState } from 'react';
21
import { Dot, DotBox, LoadingWrapper } from './styles';
32

43
const Loading: React.FC = () => {
5-
const [dotIndex, setDotIndex] = useState(0);
6-
7-
useEffect(() => {
8-
const interval = setInterval(() => {
9-
setDotIndex((prev) => prev + 1);
10-
}, 300); // 0.3초마다 업데이트
11-
12-
// 컴포넌트가 언마운트되면 interval을 정리
13-
return () => clearInterval(interval);
14-
}, []);
15-
164
return (
175
<LoadingWrapper>
186
<DotBox>
19-
<Dot $index={0} $dotIndex={dotIndex} />
20-
<Dot $index={1} $dotIndex={dotIndex} />
21-
<Dot $index={2} $dotIndex={dotIndex} />
7+
<Dot $index={0} />
8+
<Dot $index={1} />
9+
<Dot $index={2} />
2210
</DotBox>
2311
</LoadingWrapper>
2412
);

src/components/Loading/styles.tsx

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
import styled, { css } from 'styled-components';
1+
import styled, { keyframes } from 'styled-components';
2+
3+
// 점이 활성화될 때의 애니메이션 (Y축으로 움직임)
4+
const bounce = keyframes`
5+
0%, 100% {
6+
transform: translateY(0);
7+
opacity: 0.5;
8+
}
9+
50% {
10+
transform: translateY(-2px);
11+
opacity: 1;
12+
}
13+
`;
214

315
export const LoadingWrapper = styled.div`
416
position: fixed;
@@ -18,20 +30,15 @@ export const DotBox = styled.div`
1830
margin: auto;
1931
`;
2032

21-
export const Dot = styled.hr<{ $index: number; $dotIndex: number }>`
22-
width: 7px;
23-
height: 7px;
33+
export const Dot = styled.hr<{ $index: number }>`
34+
width: 8px;
35+
height: 8px;
2436
z-index: 200;
2537
border-radius: 50%;
2638
border: none;
2739
background-color: ${({ theme }) => theme.colors.gray2};
28-
transition:
29-
opacity 0.3s,
30-
transform 0.3s;
3140
32-
// 점이 활성화되었을 때의 스타일
33-
${({ $index, $dotIndex }) => css`
34-
opacity: ${$dotIndex % 3 === $index ? 1 : 0.5};
35-
transform: translateY(${$dotIndex % 3 === $index ? -1 : 0}px);
36-
`}
41+
// 각 점에 대해 딜레이를 적용하여 순차적으로 애니메이션을 시작
42+
animation: ${bounce} 0.6s ease-in-out infinite;
43+
animation-delay: ${({ $index }) => $index * 0.2}s;
3744
`;

0 commit comments

Comments
 (0)