[css, js] Parallax Scroll Effect

tobo·2022년 7월 29일
0

CSS

목록 보기
8/8
post-thumbnail

1. CSS Perspective

🎩 CSS를 활용한 Parallax Scolling은 원근법(perspective)에 의한 방식이다.
렌즈 구경(24, 50, 135mm...)과 카메라와 피사체 간의 거리를 고려하면 이해가 빠르다.


🔥 카메라를 들고 좌우 혹은 상하로 움직일 때, 거리에 따라 오브젝트의 움직임을 생각해 보자.

📸 카메라의 움직임에 따라 피사체가 카메라에 가까울수록 빠르게, 멀수록 느리게 움직인다!!! 이 점을 생각하고 Z포지션을 조정해 보자.

  • 전경front-layer: 카메라에서 제일 가까운 피사체.
  • 중경mid-layer: 중간쯤에 있는 피사체.
  • 후경(원경)back-layer: 배경과 같이 멀~리 있는 피사체.

🏃‍♂️ 편의상 Z포지션이 0인 레이어(중경)를 mid-layer로 설정하고, 후경에 레이어back-layer를 배치해보자.
후경 레이어는 앞에 있는 레이어보다 움직임이 느리게 나타난다.

# html
<div class=para-wrapper>
  
  <header class="intro">Header</header>

  <div class="para-group" id="group-1">
    <div class="para-layer back-layer">Back Layer</div>
    <div class="para-layer mid-layer">Mid Layer</div>
  </div>

  <footer class="outro">Footer</footer>
  
</div>
# css
.para-wrapper {
  height: 100vh;
  overflow-x: hidden;
  overflow-y: auto;
  /* 페이지 전체를 감싼 div에 perspective속성을 추가해야 한다. */
  perspective: 300px;  
}
.intro,
.outro {
  height: 100vh;
  background: royalblue;
  display: flex;
  justify-content: center;
  align-items: center;
}
.para-group {
  position: relative;
  height: 80vh;
  transform-style: preserve-3d;
  z-index: -1;
}
#group-1 .para-layer {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
}
#group-1 .back-layer {
  color: white;
  transform: translate3d(0, 0, -300px) scale(2.1);
  background: url(./image.jpg) no-repeat center / 100%;
}

위치상 후경에 배치된 요소는 당연히 작게 나타난다. 이를 원래 크기로 바꾸기 위해 아래 수식을 적용하면 된다.

scaleRecover = 1 + ( tranlateZ * -1) / wrapper perspective

.back-layer {
  transform: translate3d(0, 0, -300px) scale(2);
  background: url(./image.jpg) no-repeat center / cover;
}


2. Javascript Scroll Event

🎩 자바스크립틀를 이용해서 Parallax 이펙트를 만들려면 DOM에서 윈도우와 스크롤 관련 데이터를 사용하야 한다.

# html
<header class="placeholder">Header</header>
<section class="para-section">
  <div class="para-content">Parallax Moving</div>
</section>
<footer class="placeholder">Footer</footer>

# css
<style>
  .placeholder {
    height: 150vh;
    background: royalblue;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .para-section {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100vh;
    background-image: url(./image.jpg);
    background-size: 100%;
    background-position: 50% 50%;
    background-repeat: no-repeat;
  }
</style>

🔥 필요한 속성은 아래와 같이 호출할 수 있다.

  • 현재 윈도우의 높이: window.innerHeight
  • 문서가 현재 스크롤 된 Y위치: window.scrollY
  • 효과를 적용할 요소의 높이: element.offsetHeight
  • 효과를 적용할 요소의 Y위치: element.offsetTop


💫 이미지 요소가 화면 정중앙에 위치 했을 때를 0으로, 상단으로 빠져나가는 지점을 -1, 하단에 출현하는 지점을 1로 만들어주는 식을 구해보자.

  • yOffsetRatio -> -1 ~ +1 을 반환한다.
const elementHeight = paraSection.offsetHeight;

let centerOffest = window.scrollY - paraSection.offsetTop;
let yOffsetRatio = centerOffest / elementHeight;

✨ 이미지 사이즈에 따라 픽셀px 단위의 포지션 이동은 활용도가 떨어지므로, background-position-Y%를 활용해 포지션을 움직여 준다. 이때 포지션 50%를 중심으로 움직인다는걸 잊지말자.

paraSection.style.backgroundPositionY = `${50 + yOffsetRatio * 100}%`;

✌️ 움직임의 속도와 방향을 컨트롤 하기위해 increment를 곱해주면 더 좋을 것 같다.

const increment = -1;

let yOffset = 50 + yOffsetRatio * 100 * increment;
paraSection.style.backgroundPositionY = `${yOffset}%`;

📌 addEventListener()를 사용해 보자.

윈도우의 스크롤 이벤트를 받아 parallaxScroll 함수를 실행.

# js
const paraSection = document.querySelector('.para-section');

const parallaxScroll = () => {
  const elementHeight = paraSection.offsetHeight;
  const increment = -1;

  let centerOffest = window.scrollY - paraSection.offsetTop;
  let yOffsetRatio = centerOffest / elementHeight;

  let yOffset = 50 + yOffsetRatio * 100 * increment;
  console.log(yOffset);

  paraSection.style.backgroundPositionY = `${yOffset}%`;
};

window.addEventListener('scroll', parallaxScroll);

console.log("scrolled")를 찍어보면 알겠지만 스크롤 이벤트 리스너는 수많은 "scrolled"를 콘솔창에 찍을 것이다. 이거 맘에 안듬💢


😢 스크롤 이벤트 리스너는 리소스를 너무 많이 쓴다!

그래서 요소가 윈도우에 나타났을때만 스크롤 이벤트가 실행되도록 Intersection Observer API를 사용하기로 했다.💥

const options = { rootMargin: "-100px" };

const paraObserver = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      window.addEventListener("scroll", parallaxScroll);
      console.log("ON");
    } else {
      window.removeEventListener("scroll", parallaxScroll);
      console.log("OFF");
    }
  });
}, options);

paraObserver.observe(paraSection);


이상 Parallax Scroll 이펙트를 만드는 CSS버전과 JS버전 2가지를 알아봤다.
근데 GSAP이 애니메이션에 좋다던데... 한번 알아봐야겠다!

profile
"Think Now, Design Later"

0개의 댓글