요즘 사이트를 돌아다니다보면 스크롤 한 번에 div
가 통째로 슝~ 올라오는 기능을 구현한 사이트들을 심심찮게 볼 수 있었다.
그래서 나도 이런 효과를 구현해보고 싶었고, 앞으로도 자주 쓸 일이 있을 것 같아서 기록해두기로 합니다!
제가 말하는 효과는 아래 페이지가 소개하는 효과입니다!
wheel
의 기본 이벤트 제거하기window.addEventListener('wheel', function(e) {
e.preventDefault();
}, {passive: false}
)
💡
e.preventDefault()
- 브라우저에서 특정 이벤트 발동 시 기본적으로 수행되는 동작을 취소하는 함수
EventListener
의 옵션이passive: false
인 경우에만 사용 가능하다.
💡 wheel 이벤트에서
e.preventDefault()
적용하기
- 위의 코드에서
{passive: false}
를 제외하고 작성하면e.preventDefault()
를 적었음에도 불구하고 스크롤이 너무 잘 된다.- 이유) 이벤트타입
touchstart
,touchmove
,wheel
,mousewheel
에 대해서는passive
의 기본 값이true
이기 때문이다. 따라서, 명시적으로{passive: false}
를 적어주어야 한다.
wheel
의 deltaY
에 따라서 영역 이동시키기WheelEvent
에는 deltaY
라는 속성이 있는데, 우리는 다음 조건만 분기시키면 된다.
if(event.deltaY > 0) { ... }
if(event.deltaY < 0) { ... }
필자는 tailwindcss
를 사용하고 있으므로 다음과 같이 useEffect
를 사용하였다.
(position: absolute
인 div의 top
속성을 조절해주었다)
useEffect(() => {
window.addEventListener(
'wheel',
function (e) {
e.preventDefault();
const paletteWrapper = document.querySelector('#palette-wrapper') as HTMLElement;
//스크롤을 아래로 내리면 팔레트가 올라오고, 위로 올리면 팔레트가 내려간다.
if (e.deltaY > 0) {
paletteWrapper?.classList.replace('md:top-[calc(100vh)]', 'md:top-[calc(5vw+2.5rem)]');
} else {
paletteWrapper?.classList.replace('md:top-[calc(5vw+2.5rem)]', 'md:top-[calc(100vh)]');
}
},
{ passive: false },
);
});
해당 section
에 transition
까지 넣어주어 애니메이션이 아주 부드럽게 잘 구현되었다.
🧐
onScroll
도 있는데 왜 굳이onWheel
에preventDefault()
를 걸까요?상단 코드에서
wheel
이 아니라scroll
로 바꾸면 정상적으로 스크롤이 됩니다! (충격) 그래서onScroll
과onWheel
의 차이점을 계속 찾아보았어요. 그러다가 답을 얻었습니다!이해를 위해서 원문 그대로 옮기겠습니다. (참고링크)
We can’t prevent scrolling by using
event.preventDefault()
inonscroll
listener, because it triggers "after" the scroll has already happened.But we can prevent scrolling by
event.preventDefault()
on an event that causes the scroll, for instancekeydown
event forpageUp
andpageDown
.If we add an event handler to these events and
event.preventDefault()
in it, then the scroll won’t start.
위 코드에서는 window
에다가 EventListener를 달았는데, 그래서 문제가 생겼습니다.
이 스크롤을 막는 효과를 적용할 페이지에 들어갔다가 다시 다른 페이지나 홈페이지로 이동했을 때에도 스크롤이 먹통이 되어버리는 현상이 발생했습니다. (너무 당연하죠ㅜ)
그래서 스크롤을 막은 페이지에서 window
에 EventListener를 부착하지 않고, 해당 페이지의 모든 컴포넌트들을 감싸는 최상위 부모 컴포넌트를 만들어서 거기에 wheel
을 막는 EventListener를 부착하였고, 따라서 다른 페이지들에서는 당연히 정상적인 스크롤이 가능해졌습니다!