티스토리 뷰

🚀 개요

 토스 홈페이지 클론 코딩 중, Javascript만을 이용하여 스크롤 이벤트를 구현했다. 토스 홈페이지에서의 대부분의 애니메이션은 특정 요소가 화면에 보이기 시작하는 순간에 애니메이션이 시작된다. 이를 감지하기 위해 스크롤 이벤트 핸들러를 사용하였다. 그리고 이벤트 핸들러 내부에서는 화면에 요소가 나타났는지 확인을 하기 위한 조건으로서 window.outerHeight와 element.getBoundingClientRect() 을 적절하게 조합하여 사용하였다.

 

또한 요소에 걸리는 애니메이션은 opacity와 transform을 이용한 간단한 애니메이션이어서 바닐라 JS로 구현하기 적당한 난이도이다. 

 

애니메이션을 사용한 몇 가지 유형을 소개시키고자 글을 쓴다. 

 

최종 결과물은 다음 경로에서 확인 할 수 있다. 배포 링크

https://github.com/KDT1-FE/Y_FE_HTML_CSS/tree/KDT0_ChoiWuHyeok

 

GitHub - KDT1-FE/Y_FE_HTML_CSS

Contribute to KDT1-FE/Y_FE_HTML_CSS development by creating an account on GitHub.

github.com

 

🐶 기본 이벤트 원리

 스크롤 하다가 text객체가 나타나는 애니메이션을 넣고자 가정해보자.  getBoundingClientRect().top 값이 0 부터 window.innerHeight 사이인 경우에 text 객체가 화면안에 위치한다고 생각할 수 있다.

 

그래서 window 객체에 scroll 이벤트 핸들러를 붙이고, 조건문으로 위의 조건 시에 이벤트를 발생하여 객체의 CSS 속성을 바꿔주며 Animation을 부여하게 된다. 그럼 어떻게 Animation을 붙일까에 대해 얘기하기에 앞서, 앞으로 작성할 코드에 대해 최적화를 하고 넘어가자. 본 이벤트는 한번 나타나고, 그 뒤로는 사라지는 이벤트다보니, 이벤트가 한번 발생 할 경우, 이를 지워줘야한다. 따라서 if 문을 한번 통과하면 removeEventListener를 이용하여 scroll 감지를 지워준다.

 

let windowHeight = window.innerHeight

/* home 스크롤 이벤트 */
const homeText = document.querySelector('이벤트 주고 싶어하는 객체 선택')

const homeEventHandler = ()=>{
    // window 화면 상단에서 한 요소의 가장 높은 위치까지 크기와 윈도우 높이를 비교
    // 화면에 잘 보이기 위해 200px의 여유를 둠
    // console.log(homeText.getBoundingClientRect().top < windowHeight) 
    if(homeText.getBoundingClientRect().top < windowHeight-200){
        setTimeout(()=>{
        
            // 어떤 속성을 줄까?
            
        },300)
        
        // 성능 최적화를 위하여 한번 사용한 eventListener를 제거 => 딱 한번만 동작하게 됨
        window.removeEventListener('scroll',homeEventHandler)
    }
}

window.addEventListener('scroll',homeEventHandler)

 

🐶 유형1. CSS Animation이 걸려있는 요소에, JS display 속성 바꿔주기

 Display가 None일 경우 요소는 윈도우 안에 렌더링 자체가 돼있지 않는다. JS 스크롤 이벤트를 이용하여 이 객체에 display: block이나 display: inline과 같은 알맞는 display 속성을  넣어주게 된다면 기존 CSS Animation이 발동하여 화면에 나타난다. CSS Animation을 자유롭게 넣어 줄 수 있고, 렌더링을 미룰 수 있어서 성능 향상에 좋을 것으로 보인다.

 

 

 

/* 애니메이션 */
@keyframes appear{
  from {
    opacity: 0;
  }

  to{
    opacity: 1;
  }
}

@keyframes appear_from_bottom{
  0%{
    transform: translateY(150px);
    opacity: 0;
  }

  100%{
    opacity: 1;
  }
}

.text1 {
  display:none;
  animation: appear_from_bottom ease 1.5s;
}

.text2{
  display:none; 
  animation: appear;
}
let windowHeight = window.innerHeight

/* home 스크롤 이벤트 */

const homeEventHandler = ()=>{
    const Text1 = document.querySelector('.text1')
    const Text2 = document.querySelector('.text2')

    // window 화면 상단에서 한 요소의 가장 높은 위치까지 크기와 윈도우 높이를 비교

    if(Text1.getBoundingClientRect().top < windowHeight-200){
        setTimeout(()=>{
            Text1.style.display="block"
            // 화면에 시간간격마다 차례대로 화면에 요소를 띄움, 띄어지는 요소는 CSS animation 이 걸려있어서 부드럽게 동작
            setTimeout(()=>{
                Text2.style.display = "block"
            },600)
        },300)
        // 성능 최적화를 위하여 한번 사용한 eventListener를 제거 => 딱 한번만 동작하게 됨
        window.removeEventListener('scroll',homeEventHandler)
    }
}
window.addEventListener('scroll',homeEventHandler)

 

🤣 문제

 하지만 유형1과 같이 사용하다 보면 다음과 같은 문제가 생기기도 한다. 요소의 display 속성이 none이 되어 랜더링을 하지 않으므로, 이 요소에 의해 위치가 결정되는 요소의 위치가 꼬이기도 한다. 이해를 돕기 위한 아래 gif를 첨부한다. 

 

요소가 일정한 시간간격마다 차례대로 display 속성을 block으로 바꿔줘야 하는데, 요소들의 위치가 서로 종속적이다 보니, 한 요소가 나타났는데, 옆의 요소느 아직 렌더링이 안됐다 보니, 요소의 위치가 중앙으로 이동되는 것을 볼 수 있다.

 

🧐 문제 해결 시도

- Visibility 속성을 바꾸어 봄 (실패)

 위의 문제는 렌더링이 되지않아 포지션이 잡히지 않았던 것이 원인이다. 그러다보니 눈에는 안보이지만 포지션은 잡혀있는 visiblity, opacity 속성중 visiblity 속성으로 먼저 시도해 보았다. 만약, visiblity 값을 hidden에서 visible로 변경 시에 animation이 자동으로 실행 된다면 앞서 유형1을 조금만 수정하여 위와 같은 상황에 적용할 수 있다 생각했다.

 

 하지만 개발자 도구로 각 요소에 visiblity 값을 바꿔줬지만, 실제로 애니메이션이 작동하지 않아서 다음 Opacity 방식을 이용하기로 하지만, Opacity 또한 애니메이션을 걸 수 없었다.

 

🐶 유형2. Opacity 0인 요소에, 스크롤 이벤트로 animation과 Opacity 지정

 문득 예전 프로젝트 중에 animation 속성을 부여함으로 animation 효과를 준 것이 기억났다. 그래서 내가 등장효과를 주고싶은 객체에 opacity를 모두 0으로 바꿔두고, JS 스크롤 이벤트 시 그 요소에 opacity를 1로 주는 animation 속성값을 달아주는 방식으로 바꿨다. 이렇게만 하면 animation 이 동작 시에는 opacity가 1로 바뀌어 요소가 화면에 보이겠지만, animation이 끝나면 이 요소는 원래 기본값인 opacity 0으로 돌아가다 보니, 추가적으로 JS 스크롤 이벤트 실행 시 Opacity값도 1로 바꾸어 주었다.

 

최종결과

const home4EventHandler = ()=>{
    const Text = document.querySelector('.home4_textwrap')


    if(Text.getBoundingClientRect().top < windowHeight-200){
        setTimeout(()=>{
            Text.style.animation = 'appear_from_bottom ease 1.5s'
            Text.style.opacity = 1
        },200) 
        window.removeEventListener('scroll',EventHandler)
    }
}
window.addEventListener('scroll',EventHandler)

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함