Chromium 84의 Web Animations API 개선사항

프로미스로 애니메이션 조정, 교체 가능한 애니메이션으로 성능 개선, 합성 모드를 사용한 더 매끄러운 애니메이션 등

Kevin Ellis
Kevin Ellis

올바르게 사용하면 애니메이션으로 브랜드에 대한 사용자 인식과 기억을 개선하고, 사용자 작업을 안내하며, 사용자가 애플리케이션을 탐색할 수 있도록 함으로써 디지털 공간에서 맥락을 제공할 수 있습니다.

Web Animations API는 개발자가 JavaScript로 명령형 애니메이션을 작성할 수 있게 하는 도구입니다. 이 기능은 CSS 애니메이션과 전환 구현을 모두 지원하고 향후 효과를 개발할 수 있도록 작성되었으며 기존 효과를 구성하고 시간을 지정할 수 있도록 작성되었습니다.

FirefoxSafari에서는 이미 모든 사양 기능이 구현되어 있지만, Chromium 84에서는 이전에 지원되지 않았던 다양한 기능을 Chrome 및 Edge에 제공하여 브라우저 간 상호 운용성을 지원합니다.

Web Animations API는 2014년 7월 버전 36에서 처음으로 Chromium에 출시되었습니다. 이제 사양은 버전 84로 완성되어 2020년 7월에 출시됩니다.
Chromium Web Animations API의 긴 역사입니다.

시작하기

@keyframe 규칙을 사용해 보았다면 Web Animations API를 통해 애니메이션을 만드는 것이 매우 익숙할 것입니다. 먼저 키프레임 개체를 만들어야 합니다. CSS에서 다음과 같이 표시될 수 있습니다.

@keyframes openAnimation {
  0% {
    transform: scale(0);
  }
  100% {
    transform: scale(1);
  }
}

자바스크립트에서는 다음과 같습니다.

const openAnimation = [
  { transform: 'scale(0)' },
  { transform: 'scale(1)' },
];

CSS에서 애니메이션 매개변수를 설정하는 위치:

.modal {
  animation: openAnimation 1s 1 ease-in;
}

JS에서 설정합니다.

document.querySelector('.modal').animate(
    openAnimation, {
      duration: 1000, // 1s
      iterations: 1, // single iteration
      easing: 'ease-in' // easing function
    }
);

코드의 양은 비슷하지만, JavaScript를 사용하면 CSS만 사용할 수 없는 몇 가지 초능력을 얻게 됩니다. 여기에는 효과 순서 지정 기능과 재생 상태 제어 기능이 포함됩니다.

element.animate() 이후

그러나 이번 업데이트를 통해 Web Animations API는 더 이상 element.animate()를 통해 만들어진 애니메이션으로 제한되지 않습니다. CSS 애니메이션과 전환도 조작할 수 있습니다.

getAnimations()는 요소가 element.animate() 또는 CSS 규칙 (CSS 애니메이션 또는 전환)을 통해 생성되었는지와 관계없이 요소의 모든 애니메이션을 반환하는 메서드입니다. 예를 들면 다음과 같습니다.

먼저 전환 키프레임을 "get"하여 전환 위치를 결정합니다. 그런 다음 두 개의 새 불투명도 애니메이션을 만들어 크로스 페이드 효과를 사용 설정합니다. 크로스 페이드가 완료되면 사본을 삭제합니다.

프로미스로 애니메이션 조정

이제 Chromium 84에는 프로미스와 함께 사용할 수 있는 두 가지 메서드 animation.readyanimation.finished가 있습니다.

  • animation.ready를 사용하면 대기 중인 변경사항이 적용될 때까지 기다릴 수 있습니다 (즉, 재생 및 일시중지와 같은 재생 컨트롤 메서드 간 전환).
  • animation.finished는 애니메이션이 완료될 때 맞춤 JavaScript 코드를 실행하는 방법을 제공합니다.

예시를 계속 사용하면서 animation.finished를 사용하여 조정된 애니메이션 체인을 만들어 보겠습니다. 여기에서는 세로 변환 (scaleY) 후 가로 변환 (scaleX)과 하위 요소의 불투명도 변경이 이어집니다.

여는 모달 요소에 변환 및 불투명도를 적용합니다. Codepen 데모 보기
const transformAnimation = modal.animate(openModal, openModalSettings);
transformAnimation.finished.then(() => { text.animate(fadeIn, fadeInSettings)});

체인의 다음 애니메이션 세트를 실행하기 전에 animation.finished.then()를 사용하여 이러한 애니메이션을 체이닝했습니다. 이렇게 하면 애니메이션이 순서대로 나타나며, 다양한 옵션 세트 (예: 속도 및 이징)를 사용하여 다양한 타겟 요소에 효과를 적용할 수 있습니다.

CSS 내에서, 특히 고유하면서도 시퀀싱된 애니메이션을 여러 요소에 적용할 때 다시 만들기가 번거로울 수 있습니다. @keyframe를 사용하고, 애니메이션을 배치할 올바른 타이밍 비율을 정렬하고, 시퀀스에서 애니메이션을 트리거하기 전에 animation-delay를 사용해야 합니다.

예: 재생, 일시중지, 되감기

열 수 있는 것은, 닫혀야 합니다. 다행히 Chromium 39 이후로 Web Animations API를 통해 애니메이션을 재생, 일시중지 및 역방향 설정할 수 있습니다.

위의 애니메이션을 가져와서 .reverse()를 사용하여 버튼을 다시 클릭하면 부드럽고 반전된 애니메이션을 적용할 수 있습니다. 이렇게 하면 모달에 대한 보다 원활하고 상황별 상호작용을 만들 수 있습니다.

버튼 클릭 시 모달 열기 및 닫기의 예 Glitch에 관한 데모 보기

할 수 있는 작업은 재생 대기 중인 애니메이션 두 개 (openModal 및 인라인 불투명도 변환)를 만든 다음 애니메이션 중 하나를 일시중지하고 다른 애니메이션이 완료될 때까지 지연하는 것입니다. 그런 다음 프로미스를 사용하여 각 작업이 완료될 때까지 기다린 후에 재생할 수 있습니다. 마지막으로 플래그가 설정되었는지 확인한 다음 각 애니메이션을 역전시킬 수 있습니다.

예: 부분 키프레임과의 동적 상호작용

마우스 클릭으로 애니메이션을 새 위치로 조정하는 재타겟팅의 예. Glitch 관련 데모 보기
selector.animate([{transform: `translate(${x}px, ${y}px)`}],
    {duration: 1000, fill: 'forwards'});

이 예에는 하나의 키프레임만 있으며 지정된 시작 위치는 없습니다. 다음은 부분 키프레임을 사용하는 예입니다. 여기서 마우스 핸들러는 몇 가지 작업을 실행합니다. 새 종료 위치를 설정하고 새 애니메이션을 트리거합니다. 새 시작 위치는 현재의 기본 위치에서 추론됩니다.

기존 전환이 계속 실행 중일 때 새 전환이 트리거될 수 있습니다. 즉, 현재의 전환이 중단되고 새 전환이 생성됩니다.

대체 가능한 애니메이션으로 성능 개선

'mousemove'와 같은 이벤트를 기반으로 애니메이션을 만들면 매번 새 애니메이션이 생성되므로 메모리를 빠르게 소비하고 성능을 저하시킬 수 있습니다. 이 문제를 해결하기 위해 Chromium 83에서는 교체 가능한 애니메이션이 도입되어 자동 정리 기능이 사용되었습니다. 이 기능에서는 완료된 애니메이션이 교체 가능으로 표시되고 다른 완료된 애니메이션으로 교체되면 자동으로 삭제됩니다. 다음 예를 참고하세요.

마우스가 움직일 때 혜성 흔적이 애니메이션으로 표시됩니다. Glitch에 관한 데모 보기
elem.addEventListener('mousemove', evt => {
  rectangle.animate(
    { transform: translate(${evt.clientX}px, ${evt.clientY}px) },
    { duration: 500, fill: 'forwards' }
  );
});

마우스가 움직일 때마다 브라우저는 혜성 흔적에 있는 각 공의 위치를 다시 계산하여 이 새로운 지점에 애니메이션을 만듭니다. 이제 브라우저는 다음과 같은 경우 이전 애니메이션을 삭제 (대체를 활성화)할 수 있습니다.

  1. 애니메이션이 완료됩니다.
  2. 복합 순서 지정에 상위에 있는 애니메이션이 하나 이상 있으며, 이러한 애니메이션도 종료됩니다.
  3. 새 애니메이션은 동일한 속성에 애니메이션을 적용합니다.

anim.onremove를 사용하여 카운터를 트리거하여 삭제된 각 애니메이션으로 카운터를 집계하여 정확히 얼마나 많은 애니메이션이 대체되는지 확인할 수 있습니다.

애니메이션 컨트롤을 한층 더 발전시킬 수 있는 몇 가지 추가 메서드가 있습니다.

  • animation.replaceState()는 애니메이션이 활성 상태인지, 유지되는지 또는 삭제되었는지를 추적하는 수단을 제공합니다.
  • animation.commitStyles()는 합성 순서에 따라 요소의 모든 애니메이션과 함께 기본 스타일을 기반으로 요소의 스타일을 업데이트합니다.
  • animation.persist()는 애니메이션을 교체 불가능한 것으로 표시합니다.

합성 모드로 더 매끄러운 애니메이션

Web Animations API를 사용하면 이제 애니메이션의 합성 모드를 설정할 수 있습니다. 즉, 기본 모드인 '바꾸기' 외에도 애니메이션의 합성 모드를 설정할 수 있습니다. 복합 모드를 사용하면 개발자가 고유한 애니메이션을 작성하고 효과가 결합되는 방식을 제어할 수 있습니다. 이제 'replace' (기본 모드), 'add', 'accumulate' 등 세 가지 합성 모드가 지원됩니다.

애니메이션을 합성할 때 개발자는 짧고 뚜렷한 효과를 작성하고 함께 결합된 모습을 확인할 수 있습니다. 아래 예에서는 회전 및 크기 조정 키프레임을 각 상자에 적용합니다. 이때 유일한 조정은 합성 모드이고 옵션으로 추가됩니다.

기본, 추가, 합성 모드를 보여주는 데모입니다. Glitch에 관한 데모 보기

기본 'replace' 합성 모드에서는 마지막 애니메이션이 변환 속성을 대체하고 rotate(360deg) scale(1.4)에서 끝납니다. 'add'의 경우 복합 요소는 회전을 추가하고 배율을 곱하여 최종 상태는 rotate(720deg) scale(1.96)가 됩니다. 'accumulate'가 변환을 결합하여 rotate(720deg) scale(1.8)이 생성됩니다. 이러한 합성 모드의 복잡성에 대한 자세한 내용은 웹 애니메이션 사양에서 The CompositeOperation 및 CompositeOperationOrAuto enumerations를 참고하세요.

UI 요소 예시를 살펴보겠습니다.

합성된 애니메이션 두 개가 적용된 바운스 드롭다운 메뉴입니다. Glitch에 관한 데모 보기

여기서는 top 애니메이션 두 개가 합성됩니다. 첫 번째는 페이지 상단에서 슬라이드 인 효과로 메뉴 자체의 전체 높이만큼 드롭다운을 이동하는 매크로 애니메이션이고, 두 번째는 마이크로 애니메이션으로 메뉴 자체의 전체 높이만큼 드롭다운 메뉴를 이동시키는 마이크로 애니메이션으로, 하단과 충돌할 때 약간의 바운스 효과를 적용합니다. 'add' 합성 모드를 사용하면 전환이 더 원활해집니다.

const dropDown = menu.animate(
    [
      { top: `${-menuHeight}px`, easing: 'ease-in' },
      { top: 0 }
    ], { duration: 300, fill: 'forwards' });

  dropDown.finished.then(() => {
    const bounce = menu.animate(
      [
        { top: '0px', easing: 'ease-in' },
        { top: '10px', easing: 'ease-out' },
        { ... }
      ], { duration: 300, composite: 'add' });
  });

Web Animations API의 다음 단계

이러한 기능들은 오늘날의 브라우저에서는 애니메이션 기능에 흥미로운 추가 기능을 제공하며 앞으로 더 많은 기능이 추가될 예정입니다. 향후 사양에 대한 추가 자료는 다음 사양을 확인하세요.