자바스크립트에서 현재의 스크롤 위치를 원하는 엘리먼트 및 위치로 이동하는 방법을 순수 자바스크립트를 사용하여 라이브러리 없이 구현해보고자 합니다. 아래에서 자세히 알아보세요.



# 자바스크립트 스크롤 애니메이션 구현하기
먼저 스크롤을 이동하는 경우는 언제 필요할까요? 가장 많이 사용되는 경우는 내부 링크로 이동하는 경우가 되겠습니다. 즉 버튼을 클릭시 원하는 내부 엘리먼트로 이동할 경우죠.
<a href="#target">바로가기</a>

<div id="target">Hi Webisfree.com</div>

일반적으로 #id를 사용하여 이동하는 방법이 많이 쓰입니다. 일단 별도의 자바스크립트가 필요없죠. 다만 애니메이션 효과가 없이 즉시 스크롤이 해당 위치로 움직인다는 단점이 존재합니다.

그래서 애니메이션이 있는 스크롤을 아래에서 구현해보려고 합니다.
 

@ css를 사용한 애니메이션 스크롤 방법
IE 등을 제외한 브라우저에서 동작하는 css를 사용하는 방법이 존재합니다. 바로 scroll-behavior 스타일 속성입니다. 자세한 내용이 궁금하시면 아래 링크를 클릭하세요.
https://webisfree.com/2019-12-19/[css]-스크롤-이동-애니메이션-속성-scroll-behavior



! 순수 자바스크립트를 사용한 스크롤 애니메이션 구현하기

먼저 만들고자 하는 함수는 다음의 기능과 프로세스를 가져야합니다.

  • 클릭시 원하는 엘리먼트를 찾음
  • 현재 스크롤 위치와 타겟(해당 엘리먼트)의 스크롤 위치를 구함
  • 해당 위치로 스크롤을 이동하되 이동시간(duration)에 맞춰 서서히 이동
  • 완료 후 종료

위 코드의 순서대로 하나씩 코드를 구현해보도록 하겠습니다. 먼저 함수 animteScrollTo()를 만들고 사용할 파라미터를 설정합니다.

@param {string} _selector Target selector
@param {number} _duration (Option) Duration time(ms) (Default. 800ms)
@param {number} _adjust (Option) Adjustment value of position (pixel)

_selector : 선택자를 문자열로 넘겨주면 해당 엘리먼트를 이동할 위치 엘리먼트로 설정함
_duration : 이동까지의 지연 시간 설정하며 기본값은 800ms
_adjust : 이동할 위치를 + 또는 - 값의 픽셀 단위로 조정할 수 있음

여기서 _selector값은 필수이고 나머지는 옵션사항입니다. _adjust는 header등이 fixed 속성을 가져 안 보이는 경우 이 값만큼 추가할 때 사용하면 편리합니다.


! 코드 작성시 생각이 필요한 부분

코드를 작성하면서 가장 중요한 부분을 생각하며 적어보았습니다.

하나. 애니메이션 구현시 requestAnimationFrame()을 사용하여 최적화 할 것
관련 링크 바로가기 >
https://webisfree.com/2020-03-19/[자바스크립트]-requestanimationframe()을-사용하는-방법-및-예제

둘. 처음 시작할 시간에서 현재 시간을 빼기 위해 타임스탬프 값을 사용할 것 (쉽게 ms 값을 얻음)
셋. 타이밍 함수(Timing Function)에 관한 고민
 
세 번째 고민은 타이밍 함수를 반영하는 부분입니다. 완성된 함수는 선형(Linear) 방식의 함수를 사용했지만 나중에는 ease-in-out, benzier 등 다양한 방식의 타이밍 함수를 선택 가능하도록 하는 것이 필요해 보입니다.



# 완성된 스크롤링 이동 함수 보기
아래는 순수 자바스크립트로 완성된 함수 animteScrollTo()입니다.
/**
* Animate scrolling to a target position
* @param {string} _selector Target selector
* @param {number} _duration (Option) Duration time(ms) (Default. 800ms)
* @param {number} _adjust (Option) Adjustment value of position
*/
animteScrollTo = function(_selector, _duration, _adjust) {
  const targetEle = document.querySelector(_selector);
  if (!targetEle) return;

  // - Get current & target positions
  const scrollEle = document.documentElement || window.scrollingElement,
  currentY = scrollEle.scrollTop,
  targetY = targetEle.offsetTop - (_adjust || 0);
  animateScrollTo(currentY, targetY, _duration);

  // - Animate and scroll to target position
  function animateScrollTo(_startY, _endY, _duration) {
    _duration = _duration ? _duration : 600;
    const unitY = (targetY - currentY) / _duration;
    const startTime = new Date().getTime();
    const endTime = new Date().getTime() + _duration;

    const scrollTo = function() {
      let now = new Date().getTime();
      let passed = now - startTime;
      if (now <= endTime) {
        scrollEle.scrollTop = currentY + (unitY * passed);
        requestAnimationFrame(scrollTo);
      }
      else {
        console.log('End off.')
      }
    };
    requestAnimationFrame(scrollTo);
  };
};

사용 방법은 간단합니다. 만약 #test 이름의 id를 가진 엘리먼트로 이동한다면 아래와 같죠.
animteScrollTo('#test');

옵션으로 duration과 adjustY 값을 적용할 수 있으니 함께 사용하면 좋겠습니다. 테스트를 목적으로 아래 버튼 두 개를 추가하였습니다. 각각 클릭 후 잘 동작하는지 확인해보세요.