제이쿼리에서는 js로 애니메이션 효과를 구현하려면 편하게 .animate 메소드를 사용할 수 있었지만
바닐라 js로 애니메이션을 구현하려고 하면 비교적 복잡한 코드로 구현이 필요하다는걸 알 수 있다.
애니메이션 작동 원리를 생각해보면 일정한 시간 간격으로 특정 위치로 이동하게 되는 소스를 구현해야 하는데 일정 시간을 반복하는걸 생각해보면 setInterval이나 setTimeout 를 생각할 수 있는데 두 가지 방법 보다는 requestAnimationFrame이 브라우저 측면에서는 훨씬 효율적이다.
1. requestAnimationFrame 란?
requestAnimationFrame는 (줄여서 RAF라고 부른다.) 브라우저에서 제공하는 메소드로 애니메이션과 같이 반복적인 작업을 수행할 때 사용한다.
requestAnimationFrame의 특징을 설명하기 위해서는 setInterval, setTimeout 함수와 비교를 해보면 쉽게 알 수 있다.
setInterval / setTimeout | requestAnimationFrame (RAF) |
타이머의 주기가 정확하지 않기 때문에 애니메이션이 일정하지 않게 보이거나 끊겨 보일 수 있다. | 브라우저의 리페인트 주기에 맞게 콜백 함수를 예약하기 때문에 브라우저가 화면을 갱신할 때에 맞춰 애니메이션을 실행한다. 따라서 부드러운 애니메이션을 볼 수 있다. |
고정된 타이머 주기를 사용해서 타이머 기능이 필요하지 않더라도 불필요하게 작업이 되거나 배터리 소모가 된다. | 브라우저에 최적화하여 애니메이션을 처리하기 때문에 사용자 디바이스의 사양이나 배터리 수명 등을 최적화 하여 실행한다. |
브라우저가 비활성된 상태에서 혹은 백그라운드에서 실행을 멈추지 않고 계속해서 작업을 실행하기 때문에 불필요한 리소스를 소비하게 된다. | 브라우저가 비활성된 상태이거나 백그라운드에서 실행을 할 때 애니메이션 처리를 조정하여 성능을 최적화한다. |
정해진 시간 간격에 따라 비동기적으로 작업을 예약하고 실행하기 때문에 다른 브라우저 작업과 동기화가 보장되지 않을 수 있다. | 브라우저의 리페인트 주기에 맞게 콜백 함수를 실행하기 때문에 다른 브라우저 작업과 동기화 되어 실행한다. |
그리고
requestAnimationFrame은 초가 60프레임 정도를 구현한다.
(브라우저 상황에 따라 다를 수 있다. 하지만 최대한 구현하려한다.)
2. requestAnimationFrame 사용
기본적인 사용은 아래와 같다.
const callBackFnc = (timeStamp) => {
console.log(timeStamp); // timeStamp 값이 시간에 따라 계속 증가한다.
}
// requestAnimationFrame(콜백함수);
requestAnimationFrame(callBackFnc);
requestAnimationFrame(콜백함수) 로 사용하는데
이때 사용하는 콜백함수는 첫 번째 파라미터 값으로 타임 스탬프를 가져온다.
그리고 그 타임 스탬프는 시간의 경과에 따라 증가한다.
requestAnimationFrame의 간단한 사용 예시를 보자면
const testBtn = document.querySelector('.btn_test');
let testDuration = 0;
const scrollEvt = () => {
testDuration += 50;
const rafScroll = requestAnimationFrame(scrollEvt);
window.scrollTo(0, testDuration);
if(testDuration > 500){
cancelAnimationFrame(rafScroll);
testDuration = 0;
}
}
testBtn.addEventListener('click', scrollEvt);
위 코드는 실무에서 쓰일 일은 없는 소스지만 간단하게 보자면
testBtn 버튼을 누르면 브라우저의 스크롤이 내가 지정한 위치만큼 이동하게 된다.
window.scrollTo(0, 원하는 위치);
로 지정을 하는데 testDuration이라는 반복되는 값을 만들어서 requestAnimationFrame 함수가 실행 될 때 마다
50씩 증가를 시켜 스크롤 위치에 변화를 준다.
그리고 증가하는 testDuration 변수 값이 500이 넘어가게 되면 requestAnimationFrame 함수의 반대 성격의 cancelAnimationframe() 함수를 사용해서 애니메이션을 멈출 수 있다.
(버튼 클릭 -> 50씩 스크롤 위치 증가 -> 증가된 값이 500이 넘어가면 멈춤)
3. requestAnimationFrame 활용
실무에서 쓸법한 내용으로 예시를 들자면
const duration = 500; // 애니메이션 속도 조정
const moveEtc = (target) => {
const targetEl = document.querySelector(target); // 함수를 실행할 때 목적지 엘리먼트를 받아옴
if(targetEl){
let start;
const targetPosition = targetEl.getBoundingClientRect().top; // 파라미터로 받아온 target의 위치값 구하기
const startPosition = window.scrollY; // 애니메이션 이벤트가 발생했을 때의 현재 위치 값
const distance = targetPosition;
const easings = { // 애니메이션의 easing 효과
linear(t) {
return t;
},
easeInQuad(t) {
return t * t;
},
easeOutQuad(t) {
return t * (2 - t);
},
easeInOutQuad(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
},
easeInCubic(t) {
return t * t * t;
},
easeOutCubic(t) {
return (--t) * t * t + 1;
},
easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
},
easeInQuart(t) {
return t * t * t * t;
},
easeOutQuart(t) {
return 1 - (--t) * t * t * t;
},
easeInOutQuart(t) {
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
},
easeInQuint(t) {
return t * t * t * t * t;
},
easeOutQuint(t) {
return 1 + (--t) * t * t * t * t;
},
easeInOutQuint(t) {
return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
}
};
const moveScroll = function(timeStamp){ // requestAnimationFrame를 적용할 함수, 첫 번째 파라미터로 타임 스탬프 받아오기
if(start === undefined){
start = timeStamp; // requestAnimationFrame 함수의 타임스탬프를 기준으로 시작 시간을 지정
}
const elapsed = timeStamp - start; // 시작 시간, 타임 스탬프를 이용해서 애니메이션 경과 시간을 구한다.
const targetPos = startPosition + distance * easings.linear(elapsed / duration); // 현재 위치, 목표 위치, easing 등으로 최종 목적지까지의 애니메이션 형태를 구성
window.scrollTo(0, targetPos);
if(elapsed <= duration){
requestAnimationFrame(moveScroll);
}else if(timeStamp >= elapsed){
// 애니메이션 종료 후
const targetPosFix = window.scrollY + targetEl.getBoundingClientRect().top;
window.scrollTo(0, targetPosFix);
}
};
requestAnimationFrame(moveScroll);
}
}
위와 같이 작성할 수 있다.
requestAnimationFrame(콜백함수)의 콜백함수는 첫 번째 파라미터로 받아온 타임 스탬프 값(timeStamp)을 이용해서 애니메이션 시작점, 목적지, 애니메이션의 경과 시간 등을 계산한다.
참고
https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame
https://codepen.io/pawelgrzybek/pen/ZeomJB
https://velog.io/@y_jem/react-%EC%8A%AC%EB%A1%AF-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EA%B8%B0%EB%8A%A5
'Frontend > Javascript' 카테고리의 다른 글
Javascript (자바스크립트) - script defer, async (스크립트 파일 가져오기) (0) | 2024.02.20 |
---|---|
Javascript (자바스크립트) 활용 - 타이팅 효과(텍스트 입력) (0) | 2023.11.13 |
Javascript (자바스크립트) - 호이스팅 (변수 호이스팅, 함수 호이스팅) (0) | 2022.08.01 |
Javascript (자바스크립트) 플러그인 - AOS (스크롤에 따른 애니메이션 효과) (0) | 2022.03.07 |
Javascript (자바스크립트) 활용 - 팝업(popup) 구현 (0) | 2021.12.30 |