[CATSPOT]React + Recoil 기반 계단 애니메이션 구현
2025. 3. 27. 15:26ㆍ프론트엔드/REACT
728x90
컴포넌트 정의 및 내부 상태 초기화
컴포넌트 Stair는 totalFloors(전체 층 수)를 prop으로 받아 동적으로 계단을 생성합니다.
const Stair = ({ totalFloors }) => {
// 전역 상태: 선택된 층 (Recoil)
const [selectedFloor, setSelectedFloor] = useRecoilState(currentFloorState);
// 애니메이션 진행 여부 확인
const [isAnimating, setIsAnimating] = useState(false);
설명:
- selectedFloor는 현재 선택된 층을 나타내며, Recoil의 전역 상태로 관리
- isAnimating 상태는 애니메이션이 진행 중인지 여부를 판단하여 중복 실행을 방지
층 선택 및 애니메이션 시작 함수
사용자가 층을 클릭하면 애니메이션을 시작하는 함수입니다.
// 사용자가 층을 클릭했을 때 호출되는 함수
const handleClick = (floor) => {
if (floor !== selectedFloor && !isAnimating) {
setIsAnimating(true);
animateFloorChange(selectedFloor, floor);
}
};
설명:
- 조건 확인: 선택된 층과 클릭한 층이 다르고, 현재 애니메이션이 진행 중이지 않을 때만 애니메이션을 시작
- animateFloorChange 함수를 호출하여 현재 층에서 목표 층까지 이동하는 애니메이션을 진행
단계별 층 이동 애니메이션 함수
실제 애니메이션을 구현하는 함수로, 일정 간격마다 층 정보를 업데이트합니다.
// 층 이동을 단계적으로 구현하는 함수
const animateFloorChange = (startFloor, targetFloor) => {
const direction = targetFloor > startFloor ? 1 : -1; // 올라가는지 내려가는지 판단
let currentFloor = startFloor;
const stepAnimation = setInterval(() => {
currentFloor += direction;
setSelectedFloor(currentFloor);
if (currentFloor === targetFloor) {
clearInterval(stepAnimation);
setIsAnimating(false); // 애니메이션 종료
}
}, 200); // 0.2초 간격으로 층 변경
};
설명:
- 방향 결정: 목표 층이 현재 층보다 높으면 1, 낮으면 -1로 이동 방향을 설정
- setInterval: 200ms마다 currentFloor 값을 한 단계씩 증가(또는 감소)시키고, 해당 값을 Recoil 상태에 업데이트
- 애니메이션 종료: 목표 층에 도달하면 clearInterval로 반복을 멈추고, 애니메이션 상태를 false로 전환
컴포넌트 초기화 및 동적 층 생성
전체 층 수가 변경될 때마다 기본 상태로 초기화하고, 각 층을 동적으로 렌더링합니다.
// totalFloors 값이 변경될 때마다 selectedFloor 초기화
useEffect(() => {
setSelectedFloor(1);
}, [totalFloors]);
// totalFloors에 따른 층 배열 생성
const floors = Array.from({ length: totalFloors }, (_, index) => index + 1);
설명:
- useEffect를 사용해, 건물(또는 총 층 수)이 변경되면 자동으로 선택된 층을 1층으로 초기화합니다.
- Array.from을 통해 1부터 totalFloors까지의 숫자 배열을 생성하여 렌더링에 사용합니다.
6. 렌더링 및 조건부 아이콘 표시
생성한 층 배열을 기반으로 각 계단 UI를 렌더링하고, 선택된 층에만 아이콘을 표시합니다.
return (
<div className="stair-container">
{floors.map((floor, index) => (
<div
key={index}
className="stair-step"
style={{ height: `${(index + 1) * (252 / totalFloors)}px`, width: '100%' }}
onClick={() => handleClick(floor)}
>
<p className='stair-floor'>{floor}F</p>
<div className="step-decorator"></div>
{selectedFloor === floor && (
<div className="icon-container">
<img
src="/assets/icon.svg"
alt="Icon"
className="icon"
/>
</div>
)}
</div>
))}
</div>
);
};
export default Stair;
설명:
- 동적 스타일: 각 계단(stair-step)의 높이를 계산해 동적으로 적용
- 클릭 이벤트: 각 계단에 onClick 이벤트를 붙여, 사용자가 해당 층을 클릭하면 handleClick 함수가 호출
- 조건부 렌더링: 현재 선택된 층과 일치하는 경우에만 아이콘 컨테이너(icon-container)가 렌더링되어, 애니메이션 효과가 적용된 아이콘을 표시
CSS를 통한 애니메이션 효과
CSS 파일(stair.css)에서 계단과 아이콘에 적용된 스타일과 애니메이션을 살펴봅니다.
.stair-container {
margin-top: 70px;
display: flex;
flex-direction: row; /* 계단이 한 줄로 정렬 */
align-items: flex-end; /* 바닥 정렬 */
width: 100%;
height: 252px;
padding: 0px 25px;
}
.stair-step {
display: flex;
justify-content: center;
align-items: baseline;
background-color: #182981;
color: white;
font-size: 20px;
font-family: 'Adlam Display', sans-serif;
font-weight: 400;
text-align: center;
border-radius: 5px;
position: relative;
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.5); /* 입체감 추가 */
background: linear-gradient(145deg, #47578d, #182981);
}
.step-decorator {
position: absolute;
top: -5px;
left: 0;
width: 100%;
height: 16px;
background-color: #B9BFE3;
border-top-left-radius: 10px;
}
.icon-container {
position: absolute;
top: -40px; /* 아이콘을 계단 위에 위치 */
display: flex;
justify-content: center;
align-items: center;
width: 100%;
transition: top 0.5s ease-in-out; /* 위치 변경 시 애니메이션 효과 */
}
.stair-floor {
margin-top: 10px;
}
.icon {
width: 120px; /* 아이콘 크기 조절 */
margin-bottom: 20px;
animation: bounce 1.6s ease-in-out infinite; /* 아이콘 bounce 애니메이션 */
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(-20px);
}
40% {
transform: translateY(-110px);
}
60% {
transform: translateY(-70px);
}
}
설명:
- .stair-container & .stair-step:
계단 전체 컨테이너와 개별 계단의 배치, 크기, 색상, 그림자 등 기본 스타일을 정의합니다. - .step-decorator:
각 계단의 상단에 위치한 장식 요소로, 입체감을 더해줍니다. - .icon-container & .icon:
아이콘이 계단 위에 자연스럽게 등장하며, transition과 @keyframes bounce를 활용해 애니메이션 효과를 부여(통통 튀는 효과로 동적이 사용자 경험 추가)
728x90
'프론트엔드 > REACT' 카테고리의 다른 글
| QRReader 컴포넌트[FC] (0) | 2025.04.01 |
|---|---|
| [CATSPOT]층별 학교 강의실 구조 설정 & 시간표 연동 BA2 페이지 구현기 (0) | 2025.03.27 |
| React 드래그 요소가 선택 취소 후 원래 자리로 돌아가지 않는 문제 해결하기 (0) | 2025.03.26 |
| 🧩 MatchPriorityModal 구현기 – 사용자 친화적인 모달 만들기 (0) | 2025.03.25 |
| 📌 [React] 버튼 여러 번 눌림 방지하는 실무 패턴 정리 🚫🔄 (0) | 2025.03.17 |