상세 컨텐츠

본문 제목

Intersection Observer

Knowledge/Javascript

by winCow 2021. 11. 5. 10:22

본문

Intersection Observer는 타겟 요소와 상위 요소(또는 뷰포트) 사이의 변화를 비동기적으로 관찰하는 방법이다.

MDN의 예시를 참조하여 사용 방법을 익혀 보고자 한다.

 

1. HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="js_01_IntersectionObserver.css" />
    <script src="js_01_IntersectionObserver.js"></script>
    <title>Document</title>
  </head>
  <body>
    <div id="box">
      <div class="vertical">Welcome to <strong>The Box!</strong></div>
    </div>
  </body>
</html>

 

2. CSS

#box {
  background-color: rgba(40, 40, 190, 255);
  border: 4px solid rgb(20, 20, 120);
  transition: background-color 1s, border 1s;
  width: 350px;
  height: 350px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.vertical {
  color: white;
  font: 32px "Arial";
}

.extra {
  width: 350px;
  height: 350px;
  margin-top: 10px;
  border: 4px solid rgb(20, 20, 120);
  text-align: center;
  padding: 20px;
}

 

3. Javascript

const numSteps = 20.0;
let boxElement;
let prevRatio = 0.0;
let increasingColor = "rgba(40, 40, 190, ratio)";
let decreasingColor = "rgba(190, 40, 40, ratio)";
window.addEventListener(
  "load",
  () => {
    boxElement = document.querySelector("#box");
    createObserver();
  },
  false
);

function createObserver() {
  let observer;

  let options = {
    root: null,
    rootMargin: "0px",
    threshold: buildThresholdList(),
  };

  observer = new IntersectionObserver(handleIntersect, options);
  observer.observe(boxElement);
}

function buildThresholdList() {
  let thresholds = [];
  let numSteps = 20;

  for (let i = 1.0; i <= numSteps; i++) {
    let ratio = i / numSteps;
    thresholds.push(ratio);
  }
  thresholds.push(0);
  return thresholds;
}

function handleIntersect(entries, observer) {
  entries.forEach((entry) => {
    if (entry.intersectionRatio > prevRatio) {
      entry.target.style.backgroundColor = increasingColor.replace(
        "ratio",
        entry.intersectionRatio
      );
    } else {
      entry.target.style.backgroundColor = decreasingColor.replace(
        "ratio",
        entry.intersectionRatio
      );
    }
    prevRatio = entry.intersectionRatio;
  });
}

 

 

구체적으로는 아래와 같다.

const numSteps = 20.0;
let boxElement;
let prevRatio = 0.0;
let increasingColor = "rgba(40, 40, 190, ratio)";
let decreasingColor = "rgba(190, 40, 40, ratio)";
window.addEventListener(
  "load",
  () => {
    boxElement = document.querySelector("#box");
    createObserver();
  },
  false
);

먼저 필요한 전역 변수들을 선언하고, window가 load되면 id가 box인 div를 선택해 boxElement 변수에 담고, createObserver 함수를 실행한다.

 

function createObserver() {
  let observer;

  let options = {
    root: null,
    rootMargin: "0px",
    threshold: buildThresholdList(),
  };

  observer = new IntersectionObserver(handleIntersect, options);
  observer.observe(boxElement);
}

createObserver 함수에서는 Intersection Observer 객체를 만들어 observer 변수에 담는다. 이 객체는 options에 따라, 브라우저 뷰포트를 루트로 하고, root의 여백이 없으며, buildThresholdList 함수의 결과에 따라 타겟의 가시성 퍼센티지를 결정한 뒤 handleIntersect 콜백을 실행할 것이다. threshold의 기본값은 0으로, 요소가 1픽셀이라도 보이면 즉시 콜백이 실행됨을 의미한다. threshold가 1이 되면 요소의 모든 픽셀이 화면에 노출되어야만 비로소 콜백이 실행된다.

 

그렇다면 결국, 요소의 실행 시점을 결정하는 것은 buildThresholdList 함수가 되는데 이를 자세히 보면 아래와 같다.

function buildThresholdList() {
  let thresholds = [];
  let numSteps = 20;

  for (let i = 1.0; i <= numSteps; i++) {
    let ratio = i / numSteps;
    thresholds.push(ratio);
  }
  thresholds.push(0);
  return thresholds;
}

함수 내부의 for 반복문을 통해, ratio는 0.05, 0.1, 0.15, .... 1의 값을 가질 것이고, 리턴 값인 thresholds는 [0.05, 0.1, 0.15, ... 1, 0]이 된다. 즉, observer의 콜백인 handleIntersect는 box div가 보이는 부분이 5% 단위로 변할 때마다 실행될 것이다.

 

function handleIntersect(entries, observer) {
  entries.forEach((entry) => {
    if (entry.intersectionRatio > prevRatio) {
      entry.target.style.backgroundColor = increasingColor.replace(
        "ratio",
        entry.intersectionRatio
      );
    } else {
      entry.target.style.backgroundColor = decreasingColor.replace(
        "ratio",
        entry.intersectionRatio
      );
    }
    prevRatio = entry.intersectionRatio;
  });
}

콜백함수인 handleIntersect는 위와 같다. 첫 번째 매개변수는 IntersectionObserverEntry 인터페이스를, 두 번째 매개변수는 observer를 전달하도록 구성되어 있는데, IntersectionObserverEntry 인터페이스는 타겟 요소와 루트 요소 사이의 특정한 순간을 담고 있다. 이는 IntersectionObserverEntry.boundingClientRect(타겟의 경계를 사각형 모양으로 반환한다. 이 값은 getBoundingClientRect()의 실행 결과와 같다. ), IntersectionObserverEntry.intersectionRatio(intersectionRect의 비율을 반환한다.), IntersectionObserverEntry.intersectionRect(타겟의 보이는 부분을 나타내는 DOMRect를 반환한다.), IntersectionObserverEntry.isIntersecting(Intersection Observer의 루트 요소와 타겟이 교차하는지를 확인하는 boolean 값이다.), IntersectionObserverEntry.rootBounds(루트 요소의 DOMRect를 반환한다.), IntersectionObserverEntry.target(타겟 요소를 반환한다.) 등을 프로퍼티로 가지고 있다. 

따라서 첫 번째 매개변수인 entry의 intersectionRatio를 prevRatio와 비교하여, prevRatio보다 크면 increasingColor, 작으면 decreasingColor의 ratio 문자열을 entry의 intersectionRatio 값으로 바꾸고, 그 값을 타겟의 배경 색으로 설정한 뒤, 이 값을 이전 상태(prevRatio)로 바꾼다. 즉, intersectionRatio의 값에 따라(화면에 보이는 타겟의 퍼센티지에 따라) 타겟의 배경색이 "rgba(40, 40, 190, ?)" 혹은 "rgba(190, 40, 40, ?)"으로 변화하게 되는 것이다.

'Knowledge > Javascript' 카테고리의 다른 글

[Javascript] Calendar & To-do List ④ Complete &Refactoring  (0) 2021.06.07

관련글 더보기

댓글 영역