본문 바로가기

Language & Framework/실습

바닐라 자바스크립트로 스크롤 위치에 따라 변하는 애니메이션 구현하기. 아주 간단.

 

너무 평범한 사진으로 만들어서 없어 보이는데, 실제로 많은 사이트에서 사용되고 있는 애니메이션 중 하나이고 이거 하나만 응용해도 스크롤과 관련된 거의 모든 애니메이션은 구현 가능하다.

나는 이걸 어제 스크롤 값을 직접 console창에 찍어보며 최소값과 최대값을 눈대중으로 구하고 그걸 기준으로 애니메이션을 만드는 방법으로 배웠는데, 너무 미련한 방법이라고 느껴졌다. (물론 강사님의 실력 문제가 아니라 강의 듣는 사람의 수준을 고려해서 그렇게 가르치는 같다. 초급 자바스크립트 강의다.)

그래도 나름 프로그래밍인데 숫자 같은 건 모두 컴퓨터가 구하고 나는 공식만 세워서 컴퓨터한테 명령하면 되는 거 아닐까?

구글 찾아서 배워도 되겠지만 그냥 내가 생각하는대로 만들어봤다. 남들이 한 걸 전혀 참고하지 않아서 비효율적일 가능성이 있으므로 실습 카테고리에 올린다.

 

 

HTML

    <section class="scrollEvent">
        <div class="scrollEvent__line"></div>
        <img src="/IMG_0612.JPG" alt="" class="scroll__img">
        <div class="scrollEvent__line"></div>
        <img src="/IMG_5066.jpeg" alt="" class="scroll__img">
        <div class="scrollEvent__line"></div>
        <img src="/IMG_6519.jpeg" alt="" class="scroll__img">
    </section>

scrollEvent__line이라는 빈 요소가 필수다.

나와 다른 방법으로 높이를 구하면 없어도 되겠지만, 내가 만든 방식으로는 무조건 저 박스가 있어야 한다.

 

 

 

 

CSS

.scrollEvent {
  background-color: antiquewhite;
  padding: 400px 0px;
  margin-top: 100px;
  margin-bottom: 2000px;
  padding-bottom: 1000px;

  img {
    position: sticky;
    top: 100px;
    display: block;
    margin: 100px auto;
    width: 400px;
    height: 500px;
  }
}

별 거 없고 의미도 없다.

 

 

 

 

 

 

Java Script


const ScrollEventImg = document.querySelectorAll(".scroll__img");
const EventLine = document.querySelectorAll(".scrollEvent__line");

window.addEventListener("scroll", () => {
  for (let i = 0; i < ScrollEventImg.length; i++) {
    const ScrollTop = EventLine[i].getBoundingClientRect().top + scrollY;
    const ImgHeight = ScrollEventImg[i].clientHeight;
    const EventStart = scrollY - ScrollTop;
    if (EventStart >= 0) {
      ScrollEventImg[i].style.opacity = 1 - EventStart / 2 / ImgHeight;
      if (1 - EventStart / 2 / ImgHeight <= 1)
        ScrollEventImg[i].style.transform = `scale(${
          1 - EventStart / 2 / ImgHeight / 10
        })`;
    }
  }
});

 

우선 EventLine을 따로 잡아준 이유부터 보고 가자.

글재주가 없어서 그림으로 설명을 대체한다.

처음에는 sticky 때문에 내 눈에 보기에는 사진이 멈춰있지만 실은 계속 아래로 내려가면서 좌표값이 바뀌고 있다는 사실을 인지하지 못해서 사진 자체의 절대값을 기준으로 체크했다가 한참을 해맸다.

ScrollTop(요소의 top까지의 거리) - scrollY(내가 총 스크롤한 거리) / ImgHeight(요소의 높이)로 값을 구해야하는데, 요소의 절대값을 기준으로 ScrollTop 변수를 생성하면 뷰포트에 맞닿는 순간부터 scrollY와 ScrollTop의 숫자가 같이 증가해버린다.

 

 

필요한 변수 선언 부분.

 

지금까지 총 스크롤한 거리 + 현재 뷰포트와 EventLine의 거리 = 절대 좌표 (ScrollTop)

현재 스크롤한 거리에서 EventLine의 절대 좌표값만큼 뺀다. 즉, EventStart는 이미지의 상단에 닿을 때 0부터 다시 ScrollY를 체크하는 변수.

 

 

opacity는 1일 때 완전히 불투명하고 0일 때 완전히 투명하다.

EventStart의 값이 ImgHeight(요소의 길이)와 완전히 같아지면 1이 된다.

따라서 요소의 시작부분일 때 opacity가 1에서 시작해서 끝에 도달하면 0으로 변하며 완전히 투명해짐.

 

다만 나는 이미지가 겹쳐지기 전에 완전히 투명해지는 것보다 반 쯤 투명해진 상태로 겹쳐지는 게 보기 좋다고 생각해서 /2로 나눠버렸다.

그러면 요소의 시작부분일 때 opacity가 1 , 끝에 도달하면 0.5가 된다.

 

 

scale의 경우는 '내가 원하는 값'이 1보다 작을 때 실행되도록 조건문을 만들어줘야 한다.

opacity는 1보다 높은 수가 의미가 없지만 scale은 1보다 높으면 요소가 커지기 때문이다.

물론 의도적으로 사진이 입체적으로 회전하며 사라지는 것 같은 애니메이션을 연출하고 싶으면 요소가 큰 상태에서 작아지는 것도 나쁘지 않다.

 

나는 크기를 많이 줄이고 싶은 생각이 없었고 0.95 정도로만 줄이고 싶어서 10으로 다시 한 번 나눠줬다.

끝. 아주 간단하다.

 

물론 나는 이 간단한 걸 만드는데 하루가 걸렸다 : (