원활한 시작 및 종료 애니메이션을 위한 4가지 새로운 CSS 기능

모션은 사용자를 한 상호작용에서 다음 상호작용으로 안내하는 모든 디지털 환경의 핵심 부분입니다. 하지만 웹 플랫폼에서는 부드러운 애니메이션에 몇 가지 간격이 있습니다. 여기에는 들어가기 및 나가기 애니메이션을 쉽게 애니메이션 처리하고 대화상자 및 팝오버와 같은 닫을 수 있는 요소의 최상위 레이어로 부드럽게 애니메이션 처리하는 기능이 포함됩니다.

이러한 격차를 메우기 위해 Chrome 116 및 117에는 개별 속성의 매끄러운 애니메이션과 전환을 가능하게 하는 네 가지 새로운 웹 플랫폼 기능이 포함되어 있습니다.

새로운 기능 4가지에는 다음이 포함됩니다.

  • 키프레임 타임라인에서 displaycontent-visibility에 애니메이션을 적용하는 기능(Chrome 116부터)
  • display와 같은 개별 속성의 전환을 사용 설정하기 위한 allow-discrete 키워드가 있는 transition-behavior 속성(Chrome 117부터)
  • display: none에서 최상위 레이어로 항목 효과를 애니메이션 처리하는 @starting-style 규칙 (Chrome 117부터)
  • 애니메이션 중에 최상위 레이어 동작을 제어하는 overlay 속성(Chrome 117부터)

키프레임에 애니메이션 표시

Chrome 116부터 키프레임 규칙에서 displaycontent-visibility를 사용할 수 있습니다. 그러면 키프레임이 발생할 때 교체됩니다. 이를 지원하기 위해 추가로 새 값을 만들 필요는 없습니다.

.card {
  animation: fade-out 0.5s forwards;
}

@keyframes fade-out {
  100% {
    opacity: 0;
    display: none;
  }
}
를 참고하세요.

위 예에서는 0.5초 동안 불투명도를 0으로 애니메이션 처리한 다음 디스플레이를 none으로 설정합니다. 또한 forwards 키워드는 애니메이션이 종료 상태로 유지되도록 하므로 애니메이션이 적용되는 요소는 display: noneopacity: 0로 유지됩니다.

다음은 전환으로 할 수 있는 작업을 모방한 간단한 예입니다(전환 섹션의 데모 참고). 그러나 전환으로는 다음 예와 같이 더 복잡한 애니메이션을 만들 수 없습니다.

.card {
  animation: spin-and-delete 1s ease-in forwards;
}

@keyframes spin-and-delete {
  0% {
    transform: rotateY(0);
    filter: hue-rotate(0);
  }
  80% {
    transform: rotateY(360deg);
    filter: hue-rotate(180deg);
    opacity: 1;
  }
  100% {
    opacity: 0;
    display: none;
  }
}

spin-and-delete 애니메이션은 나가기 애니메이션입니다. 먼저 카드가 y축을 중심으로 회전하고 색조 회전을 실행한 다음 타임라인을 통해 80%에서 불투명도를 1에서 0으로 전환합니다. 마지막으로 카드가 display: block에서 display: none로 전환됩니다.

이러한 종료 애니메이션은 요소에 직접 적용하는 대신 애니메이션의 트리거를 설정할 수 있습니다. 예를 들어 애니메이션을 적용하도록 클래스를 트리거하는 버튼에 이벤트 리스너를 연결합니다.

.spin-out {
   animation: spin-and-delete 1s ease-in forwards;
}
document.querySelector('.delete-btn').addEventListener('click', () => {
 document.querySelector('.card').classList.add('spin-out');
})
를 참고하세요.

이제 위의 예시에서 최종 상태는 display:none입니다. 더 나아가 애니메이션이 먼저 완료되도록 시간 초과가 있는 DOM 노드를 제거해야 하는 경우가 많습니다.

개별 애니메이션 전환

키프레임으로 불연속 속성을 애니메이션 처리할 때와 달리 불연속 속성을 전환하려면 allow-discrete 전환 동작 모드를 사용해야 합니다.

transition-behavior 속성

allow-discrete 모드는 불연속 전환을 가능하게 하며 transition-behavior 속성의 값입니다. transition-behaviornormalallow-discrete의 두 가지 값을 허용합니다.

.card {
  transition: opacity 0.25s, display 0.25s;
  transition-behavior: allow-discrete; /* Note: be sure to write this after the shorthand */
}

.card.fade-out {
  opacity: 0;
  display: none;
}
을 참고하세요.
참고: 이 전환 데모는 첫 번째 애니메이션 데모와는 다른 기법을 보여주지만 시각적으로는 비슷합니다.

transition 약어도 이 값을 설정하므로 속성을 생략하고 각 전환의 transition 약어 끝에 allow-discrete 키워드를 사용할 수 있습니다.

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

여러 개의 개별 속성을 애니메이션 처리하는 경우 애니메이션 처리할 각 속성 뒤에 allow-discrete를 포함해야 합니다. 예를 들면 다음과 같습니다.

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

진입 애니메이션의 @starting-style 규칙

지금까지 이 도움말에서는 종료 애니메이션을 다뤘습니다. 진입 애니메이션을 만들려면 @starting-style 규칙을 사용해야 합니다.

@starting-style를 사용하여 페이지에서 요소가 열리기 전에 브라우저에서 조회할 수 있는 스타일을 적용합니다. 이 상태가 (애니메이션이 시작되는 위치) 'open-open' 상태입니다.

/*  0. IS-OPEN STATE   */
/*  The state at which the element is open + transition logic */
.item {
  height: 3rem;
  display: grid;
  overflow: hidden;
  transition: opacity 0.5s, transform 0.5s, height 0.5s, display 0.5s allow-discrete;
}

/*  1. BEFORE-OPEN STATE   */
/*  Starting point for the transition */
@starting-style {
  .item {
    opacity: 0;
    height: 0;
  }
}

/*  2. EXITING STATE   */
/*  While it is deleting, before DOM removal in JS, apply this
    transformation for height, opacity, and a transform which
    skews the element and moves it to the left before setting
    it to display: none */
.is-deleting {
  opacity: 0;
  height: 0;
  display: none;
  transform: skewX(50deg) translateX(-25vw);
}

이제 TODO 목록 항목의 진입 상태와 종료 상태가 모두 있습니다.

최상위 레이어로 또는 최상위 레이어에서 요소에 애니메이션 적용

요소를 최상위 레이어로 이동하거나 최상위 레이어에서 이동하는 애니메이션을 적용하려면 'open' 상태에서 @starting-style를 지정하여 브라우저에 애니메이션을 적용할 위치를 알립니다. 대화상자의 경우 열린 상태는 [open] 속성으로 정의됩니다. 팝오버의 경우 :popover-open 가상 클래스를 사용합니다.

대화상자의 간단한 예는 다음과 같습니다.

/*   0. IS-OPEN STATE   */
dialog[open] {
  translate: 0 0;
}

/*   1. BEFORE-OPEN STATE   */
@starting-style {
  dialog[open] {
    translate: 0 100vh;
  }
}

/*   2. EXIT STATE   */
dialog {
  transition: translate 0.7s ease-out, overlay 0.7s ease-out allow-discrete, display 0.7s ease-out allow-discrete;
  translate: 0 100vh;
}
를 참고하세요.

다음 예에서는 진입 효과와 종료 효과가 다릅니다. 뷰포트 하단에서 위로 애니메이션을 적용하여 들어가고 뷰포트 상단으로 효과를 종료합니다. 또한 시각적 캡슐화를 위해 중첩된 CSS로 작성됩니다.

팝오버에 애니메이션을 적용할 때는 이전에 사용한 open 속성 대신 :popover-open 가상 클래스를 사용하세요.

.settings-popover {
  &:popover-open {
    /*  0. IS-OPEN STATE  */
    /*  state when popover is open, BOTH:
        what we're transitioning *in* to 
        and transitioning *out* from */
    transform: translateY(0);
    opacity: 1;

    /*  1. BEFORE-OPEN STATE  */
    /*  Initial state for what we're animating *in* from, 
        in this case: goes from lower (y + 20px) to center  */
    @starting-style {
      transform: translateY(20px);
      opacity: 0;
    }
  }
  
  /*  2. EXIT STATE  */
  /*  Initial state for what we're animating *out* to , 
      in this case: goes from center to (y - 50px) higher */
  transform: translateY(-50px);
  opacity: 0;
  
  /*  Enumerate transitioning properties, 
      including display and allow-discrete mode */
  transition: transform 0.5s, opacity 0.5s, display 0.5s allow-discrete;
}
를 참고하세요.

숙박 시설 overlay

마지막으로 상단 레이어에서 popover 또는 dialog를 페이드아웃하려면 overlay 속성을 전환 목록에 추가합니다. popoverdialog는 상위 클립과 변환을 이스케이프하고 콘텐츠를 최상위 레이어에 배치합니다. overlay를 전환하지 않으면 요소가 즉시 클립되고, 변환되고, 가려지며, 전환이 표시되지 않습니다.

[open] {
  transition: opacity 1s, display 1s allow-discrete;
}

대신 전환 또는 애니메이션에 overlay를 포함하여 나머지 기능과 함께 overlay를 애니메이션 처리하고 애니메이션 처리 시 overlay가 최상위 레이어에 유지되도록 합니다. 이렇게 하면 훨씬 더 부드럽게 보입니다.

[open] {
  transition: opacity 1s, display 1s allow-discrete, overlay 1s allow-discrete;
}
을 참고하세요.

또한 최상위 레이어에 여러 요소가 열려 있는 경우 오버레이를 사용하면 최상위 레이어의 원활한 전환을 제어할 수 있습니다. 이 간단한 예에서 차이를 확인할 수 있습니다. 두 번째 팝오버를 닫을 때 overlay를 적용하지 않으면 먼저 최상위 레이어에서 나가 다른 팝오버 뒤로 이동한 후 전환을 시작합니다. 이는 그다지 매끄러운 효과가 아닙니다.

뷰 전환에 관한 참고사항

DOM에서 요소를 추가 및 삭제하는 등 DOM을 변경하는 경우 뷰 전환도 원활한 애니메이션을 위한 좋은 솔루션입니다. 다음은 뷰 전환을 사용하여 빌드된 위 예시 중 두 가지입니다.

이 첫 번째 데모에서는 @starting-style 및 기타 CSS 변환을 설정하는 대신 뷰 전환이 전환을 처리합니다. 뷰 전환은 다음과 같이 설정됩니다.

먼저 CSS에서 각 카드에 개별 view-transition-name를 지정합니다.

.card-1 {
  view-transition-name: card-1;
}

.card-2 {
  view-transition-name: card-2;
}

/* etc. */

그런 다음 JavaScript에서 뷰 전환에 DOM 변형을 래핑 (이 경우에는 카드 제거)합니다.

deleteBtn.addEventListener('click', () => {
  // Check for browser support
  if (document.startViewTransition) {
    document.startViewTransition(() => {
      // DOM mutation
      card.remove();
    });
  } 
  // Alternative if no browser support
  else {
    card.remove();
  }
})

이제 브라우저에서 각 카드의 페이드 아웃과 모핑을 새 위치로 처리할 수 있습니다.

목록 항목 추가/삭제 데모에서도 이 방법이 유용하게 사용됩니다. 이 경우 생성된 각 카드에 고유한 view-transition-name를 추가해야 합니다.

를 참고하세요.

결론

이러한 새로운 플랫폼 기능을 통해 웹 플랫폼에서 원활한 진입 및 종료 애니메이션을 구현하는 데 한 걸음 더 가까워졌습니다. 자세한 내용은 다음 링크를 확인하세요.