저번 글에 이어서 휠 이벤트(= 스크롤) 시 화면이 fadeInOut 이 되면서 페이지가 전환되게 구현해보았다.
구현 포인트
- 마우스 휠 사용 시 fadeInOut 이 되면서 한 페이지 씩 넘어가기
handleScrollVertical 함수
- handleScrollVertical함수는 useCallback을 통해 메모이제이션되므로, 동일한 함수 참조가 유지된다. ( useCallback을 사용 안 하면 컴포넌트가 리렌더링 될 때마다 handleScrollVertical 함수가 새로 생성된다. useEffect 내부의 클린업 함수가 이전 함수와 같은 참조를 가지지 않기 때문에 이벤트 리스너를 정확히 제거하지 못 할 수 있다.)
- event.deltaY값을 사용하여 사용자가 스크롤 하는 방향을 결정합니다 (event.deltaY가 양수면 아래로 스크롤, 음수면 위로 스크롤).
- setVisibleIndex를 호출하여 visibleIndex 상태를 업데이트합니다. 이때 newIndex가 0과 2 사이의 값으로 제한한다. (총 갯수에서 - 1)
const handleScrollVertical = useCallback(
(event) => {
event.preventDefault();
const scrollAmount = event.deltaY > 0 ? 1 : -1;
setVisibleIndex((prevIndex) => {
const newIndex = Math.max(0, Math.min(prevIndex + scrollAmount, 2)); // 3개의 항목(인덱스 0~2)
return newIndex;
});
},
[setVisibleIndex]
);
스크롤 실행
- useEffect를 사용하여 컴포넌트가 마운트될 때 wheel 이벤트 리스너를 추가하고, 언마운트될 때 이벤트 리스너를 제거합니다.
- passive: false 옵션을 사용하여 event.preventDefault()를 호출할 수 있도록 한다.
{ passive: false } 옵션은 이벤트 리스너에 전달되는 설정 객체의 일부로, 브라우저가 해당 이벤트 리스너에서 event.preventDefault() 호출할 수 있어 스크롤 이벤트에서 중요하다.
여기 코드에서 passive: false를 사용하는 이유 handleScrollVertical 함수에서 event.preventDefault()를 호출하기 때문이다. handleScrollVertical 함수의 event.preventDefault()를 통해 기본 스크롤 동작을 막고, 대신 커스텀 스크롤 동작을 구현하려는 것이다. passive: false가 없으면 event.preventDefault()가 호출되더라도 기본 스크롤 동작을 막지 못할 수 있다.
useEffect(() => {
const horizontalDiv = horizontalDivRef.current;
if (horizontalDiv) {
horizontalDiv.addEventListener('wheel', handleScrollVertical, { passive: false });
}
return () => {
if (horizontalDiv) {
horizontalDiv.removeEventListener('wheel', handleScrollVertical);
}
};
}, []);
FadeInOut 효과
- section(fade_container) 요소에 horizontalDivRef를 연결하여 useRef가 해당 DOM 요소를 참조할 수 있게 한다.
- div 요소의 style 속성을 활용하여 면 전체를 덮도록 설정합니다.
<section ref={horizontalDivRef} className="fade_container">
{[1, 2, 3].map((item, index) => (
<div
key={index}
className={`fade_wrap ${visibleIndex === index ? 'visible' : 'invisible'}`}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
transition: 'opacity 0.5s',
opacity: visibleIndex === index ? 1 : 0,
backgroundColor:
index === 0 ? 'pink' : index === 1 ? 'purple' : 'green',
}}
>
{item}
</div>
))}
</section>
.fade_container {
@apply overflow-hidden whitespace-nowrap relative w-screen h-screen;
}
.visible {
opacity: 1;
}
.invisible {
opacity: 0;
}
전체 코드
import { useCallback, useRef, useState, useEffect } from 'react'
export default function Test() {
const horizontalDivRef = useRef(null)
const [visibleIndex, setVisibleIndex] = useState(0)
const handleScrollFade = useCallback(
(event) => {
event.preventDefault()
const scrollAmount = event.deltaY > 0 ? 1 : -1
setVisibleIndex((prevIndex) => {
const newIndex = Math.max(0, Math.min(prevIndex + scrollAmount, 2)) // 3개의 항목(인덱스 0~5)
return newIndex
})
},
[setVisibleIndex],
)
useEffect(() => {
const horizontalDiv = horizontalDivRef.current
if (horizontalDiv) {
horizontalDiv.addEventListener('wheel', handleScrollFade, {
passive: false,
})
}
return () => {
if (horizontalDiv) {
horizontalDiv.removeEventListener('wheel', handleScrollFade)
}
}
}, [])
return (
<>
<section ref={horizontalDivRef} className="fade_container">
{[1, 2, 3].map((item, index) => (
<div
key={index}
className={`fade_wrap ${visibleIndex === index ? 'visible' : 'invisible'}`}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
transition: 'opacity 0.5s',
opacity: visibleIndex === index ? 1 : 0,
backgroundColor:
index === 0 ? 'pink' : index === 1 ? 'purple' : 'green',
}}
>
{item}
</div>
))}
</section>
</>
)
}
결과
'Javascript' 카테고리의 다른 글
FullPage 스크롤 구현 (0) | 2024.10.13 |
---|---|
file-saver 적용 (0) | 2024.09.10 |
fill() 메서드 (0) | 2024.09.09 |
비디오 addEventListener (0) | 2024.09.09 |
특정 문자 포함 여부 정규식 (0) | 2024.09.09 |