[Vue3] scroll 이벤트에 따른 헤더 표시 with Throttle

이전에 공부한 이벤트 Throttle과 스크롤 이벤트를 이용하여 스크롤 방향에 따라 헤더를 어떻게 보여줄지 결정하는 로직을 구현해 보았다.
기존에 만들던 웹 프로젝트에 기능을 추가하였다.

프로젝트 환경 및 구현 방법

Vue3 를 사용중이며, App.vue에 Header컴포넌트와 router-view 등이 자리잡고 있다.
기능을 추가하기 위해서 App.vue와 Header.vue만 수정하였다.

	src
    	- App.vue
    	- views
    		- ...
    	- component
	    	- ...
    		- Header.vue
      

App에서 Mounted가 되면 스크롤 이벤트 핸들러에서 스크롤 방향을 구하고, 방향에 대한 정보를 Header 컴포넌트에 props로 넘겨주어 Header 컴포넌트를 어떻게 보여줄 것인지 결정할 수 있도록 구현하였다.
Header에 한번에 구현해도 되지만 props를 넘겨주는 예제를 만들어보고 싶어서 이렇게 구현하였다.

Vue3 Throttle + Scroll Event Handler 구현

이벤트 쓰로틀은 이벤트 핸들러의 실행을 제한함으로써 성능 개선을 노릴(?) 수 있는 기법이다. 굳이 너무 많은 핸들러를 실행하지 않아도 된다고 생각하여 1s, 500ms, 300ms, 100ms 까지 테스트를 해보면서 제일 마음에 드는 동작을 보여주는 100ms로 설정하였다.

	// App.vue

	export default {
     	setup(){
          // 스크롤 방향 정보
          const scrollDirection = ref(0); // 1: up, -1 : down
          // Throttle을 위한 ref
          const waiting = ref(false);
          // 이전 스크롤 방향을 저장하기 위한 ref
          const scrollTrace = ref(0);
          
          // Throttle이 적용된 이벤트 핸들러
          function scrollHandler(e){
            if(!waiting.value){
              	waiting.value = true; // throttle check
            
            	const currentScroll = window.scrollY; // current scrollY
            	// scroll down = -1
            	if(currentScroll > scrollTrace.value) scrollDirection.value = -1;
            	// scroll up = 1
            	else if(currentScroll < scrollTrace.value) scrollDirection.value = 1;
            	scrollTrace.value = currentScroll;
            
            	// Event Throttle 100 ms
            	setTimeout(()=>{
                	waiting.value = false;
                }, 100); 
            }
          }
          
          onMounted(()=>{
            // DOM이 마운트 되었을 때 이벤트 핸들러를 등록한다.
            document.addEventListener('scroll',scrollHandler);
          });
          
          return {
           	 scrollDirection
          }
        }
    }

이벤트에 쓰로틀을 걸어주어 스크롤이 계속 발생하더라도 100ms 주기로 핸들러가 실행이 되도록 하였다.
현재 scrollY와 이전의 scrollY를 비교하여 현재 값이 더 크다면 스크롤을 아래로 내리고 있다는 것이다.
반대의 경우는 스크롤을 올리고 있다는 것이다.

Header컴포넌트에 props 넘겨주기

	<!--App.vue-->
	<template>
	    	<Header :scrollDirection="scrollDirection" />  
	</template>

App.vue에서 Header컴포넌트로 props를 통해 scroll 방향에 대한 정보를 넘ㄴ겨주었다.

	
	// Header.vue
	export default{
     	props:{
       		scrollDirection:Number,
       },
    	setup(props){
         	const headerShownClass=computed(()=>{
            	if(props.scrollDirection === -1) return 'header_off';
             	return '';	
           	});
	
            return {
            	headerShownClass 
         	}
      	 }, 
     
   	}

Header 컴포넌트에서 props로 scrollDirection을 받아 header의 위치를 조정하는 class를 넣을지 뺄지 결정한다.

<template>
	<header class="header" :class="headerShownClass">
    	...  
  	</header>
</template>

<style scoped>
  .header{
      position:fixed;
      top:0;
      transition: top 0.2s ease-in-out;
      height: var(--size-header);
      ...
  }
  .header_off{
  	  top: calc(-1 * var(--size-header));
  }
</style>

ref를 이용하여 직접 classList에 추가하는 방법도 있지만, vue에서는 computed 속성을 이용하여 class를 바인딩해주면 편하다.

정리

인턴을 하면서 웹페이지를 만들고 있는데, 모바일 환경까지 고려해야하기 때문에 헤더가 고정적으로 위치해 있는 것이 생각보다 공간을 많이 차지해서 컨텐츠가 보여지는 영역이 많이 줄어들었다.
스크롤의 방향을 감지하여 내리는 도중에는 헤더를 감춰주는 편이 사용자 경험이 더 좋다고 생각하였고, 대표님께 말씀드려서 바로 반영해 보았다.
좀더 디테일한 조건을 추가해 응용해 볼 수 있을 것 같다.

좋은 웹페이지 즐겨찾기