- 스크롤 시 페이드 인(Fade IN) 애니메이션 구현
- 코드 설명
- 성능
- Observer API를 사용한 스크롤 시 페이드 인(Fade IN) 애니메이션 구현
- 코드 설명
- 성능 비교
DOM 객체가 화면에 나타날 시 Fade IN 애니메이션 동작 구현하기 위해 다음과 같은 데이터의 정보 값이 필요하다.
다행히도 javascript에서는 element.getBoundingClientRect()
이라는 메서드를 제공하고 메서드 설명은 아래와 같다.
Element.getBoundingClientRect()
위의 메서드는 DOMRect요소의 크기와 브라우저 뷰포트에 상대적인 위치에 대한 정보를 제공하는 객체를 반환합니다.
getBoundingClientRect()
의 리턴 값은 해당 dom의 widh, height, left, top, right, bottom, x, y를 알려주며 width와 height는 뷰포트의 왼쪽 상단을 기준으로 합니다.mdn - element.getBoundingClientRect() : 해당 링크 들어가서
getBoundingClientRect()
이 어떻게 동작하는 지 확인해보세요
Q. 화면에 해당 DOM이 있다는 것은 어떻게 알 수 있을까??
A. 화면에 보여지는 스크롤 뷰 포트 사이즈가 getBoundingClientRect().top
요소보다 크게 된다면 우리는 해당 dom을 화면에 노출되었다는 것을 확인할 수 있다.
( codesandbox 화면을 스크롤해 밑에 찍히는 콘솔로그와 비교하면서 한번 확인해 보세요!! )
// 화면에 해당 dom이 있는 지 확인하는 변수 - 0보다 클 경우 yes
var distInView = elem.getBoundingClientRect().top - window.innerHeight + 20;
하지만 getBoundingClientRect()
함수는 해당 값을 계산할 때 리플로우(Reflow) 동작이 다시 발생하여 복잡한 앱의 경우 성능적인 문제도 발생할 수 있다는 단점이 존재한다.
Q. 성능 개선 방법은 어떤 것이 있을까?
A1. DOM 속성 변경 코드를 그룹핑하여 한번에 변경하기
- 브라우저는 기본적으로 리플로우를 줄이기 위해서 요소의 변경을 지금 당장 처리하지 않고 큐에 저장해 뒀다가 일정시간이 지나거나 처리할 변경 작업이 일정량 쌓였을 때 리플로우를 수행하는 것이다.
자세한 내용은 리플로우 리페인트 최적화 이곳을 참조바란다.
하지만 위와 같은 방법은 scroll 이벤트에 따라 변하는 getBoundingClientRect().top
의 값을 미리 선언하고 계산하는 방법은 통하지 않을 것이다.
A2. Intersection Observer API 로 성능 개선하기
브라우저의 스크롤 이벤트는 단시간 에 수백 번, 수천 번 호출될 수 있고, 동기적으로 실행되기 때문에 메인 스레드의 성능에도 영향을 줄 수 있다는 문제점과
getBoundingClientRect()
함수의 리플로우 문제점을 개선하고 2016년 4월 Intersection Observer API를 새로 구현하였다.추가적으로 Intersection Observer API는
getBoundingClientRect()
로 엘리먼트의 상대적 위치를 구하는 방법이 아닌 리플로우가 적용되지 않는IntersectionObserverEntry
객체의 여러 값들을 통해 엘리먼트의 상대적 위치를 구한다.
자세한 내용은 Intersection Observer API의 사용법과 활용방법 링크를 참조 바란다.
Intersection Observer API는 타겟 엘리먼트가 조상 엘리먼트 또는 최상위 문서의 뷰포트의 교차영역에서 발생하는 변화를 비동기
로 관찰하는 방법을 제공합니다.
// 아래의 옵션으로 뷰포트가 무엇인지, 그리고 교차영역은 어떻게 할 것인지 정해줍니다.
const options = {
root: null, // viewport
rootMargin: "0px",
threshold: 1.0 // 50%가 viewport에 들어와 있어야 callback 실행
};
// new IntersectionObserver객체의 콜백함수를 인자로 줍니다.
// 이때 entries는 스크롤 이벤트를 적용할 모든 dom 객체입니다.
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
// 해당 dom이 교차영역에 진입 시 적용하고 싶은 로직 작성
if (entry.isIntersecting) {
entry.target.classList.add("inView");
}
});
}, options);
const boxList = document.querySelectorAll(".tile");
// 반복문을 돌려 모든 DOM에 스크롤 이벤트 적용
boxList.forEach((el) => observer.observe(el));
앞서 Intersection Observer API로 스크롤 시 페이드인 구현한 앱의 성능을 확인해보자.
위의 사진과 비교, Observer API를 사용하지 않은 앱의 경우 scroll 할 때 fadeIN 함수가 동작하고 그에 따라서 getBoundingClientRect()
에 의해 리플로우 현상이 발생했지만 Observer API를 사용한 앱의 경우 스크롤 이벤트 동작 시 add함수(classList.add) 동작 시 리플로우 동작이 일어나지 않는다.
위와 같은 이유로 간단한 스크롤 이벤트 구현 시에는 Intersection Observer API 사용하는 것이 좋아보인다.
참고자료
리플로우 리페인트 최적화
Intersection Observer API 사용방법
스크롤시 페이드인 효과 구현 feat: javascript