블로그 개발

스크롤 시 헤더를 숨기고 보이는 동작을 추가했다.

블로그 개발 19 2025. 12. 19. 2025. 12. 27.

결과부터 보자면 이런 것이다.

(스크롤을 올리거나 스크롤을 맨 위로 올렸을 때)

(스크롤을 내렸을 때)

스크롤에 맞춰 자연스럽게 내려오고 올라오게 하고 싶었지만, 헤더를 상정하고 짜둔 여러 디자인들이 좀 충돌되기도 하고 계산하기도 귀찮아서 그냥 메인 영역 부분에 transform: translateY(-80px); 를 넣어주고 헤더는 아마 top으로 했을거임. 암튼 그렇게 해서 적용되도록 했다.

body에 클래스가 붙거나 떨어지는 식으로 동작한다.

또한 모바일과 PC의 사용 환경을 고려하여, PC의 경우 헤더가 좀 더 오래 머무를 수 있도록 스크롤 임계값을 높였고, 모바일에서는 다소 낮췄다.

아래는 스크롤 동작에 대해 클래스를 붙여주고 빼주는 코드이다.

...
    const SCROLL_UP_THRESHOLD = 20;
    const HEADER_HEIGHT = page.data.hideHeader ? 0 : 80;
    const FOOTER_HEIGHT = page.data.hideHeader ? 0 : 208;
    
    let windowWidth = $state(960);
    let scrollHide_threshold = $derived(!page.data.hideHeader && windowWidth > 959 ? 200 : SCROLL_UP_THRESHOLD);

    let body: HTMLElement | undefined = $state();

    let ticking = false;

    let deltaY = $state(0);
    let lastKnownScrollPosition = $state(0);
    let scrollflag = page.data.hideHeader ? true : false;
    let scrolldowned = $derived(deltaY > scrollHide_threshold && window.scrollY > HEADER_HEIGHT);


    function updateScrollFlag () {
        if (!scrollflag && scrolldowned && document.documentElement.scrollHeight > HEADER_HEIGHT + document.documentElement.clientHeight + FOOTER_HEIGHT) scrollflag = true;
        if (lastKnownScrollPosition < HEADER_HEIGHT || deltaY < -SCROLL_UP_THRESHOLD) scrollflag = false;
    }

    onMount(()=>{
        windowWidth = window.innerWidth;
        if (page.data.hideHeader) body?.classList.add("scrolldown");
    });

</script>
<svelte:window onscroll={(e)=>{
    // Event throttling
    if (!ticking) {
        requestAnimationFrame(() => {
            deltaY = window.scrollY - lastKnownScrollPosition;
            lastKnownScrollPosition = window.scrollY;
    
            updateScrollFlag();

            scrollflag ? body?.classList.add("scrolldown") : body?.classList.remove("scrolldown");
            
            ticking = false;
        })
    
        ticking = true;
    }
}} onresize={(e)=>{
    // Event throttling
    if (!ticking) {
        requestAnimationFrame(() => {
        windowWidth = window.innerWidth;
            ticking = false;
        })
    
        ticking = true;
    }
}}/>
<svelte:body bind:this={body} />
...

이정도가 최선인듯 싶다.