Четыре новые функции CSS для плавной анимации входа и выхода.

Движение — это основная часть любого цифрового опыта, направляющая пользователя от одного взаимодействия к другому. Но есть несколько пробелов в плавной анимации на веб-платформе. К ним относятся возможность легко анимировать анимацию входа и выхода, а также плавную анимацию к верхнему слою и обратно для отклоняемых элементов, таких как диалоговые окна и всплывающие окна.

Чтобы восполнить эти пробелы, Chrome 116 и 117 включают четыре новые функции веб-платформы, которые обеспечивают плавную анимацию и переходы для отдельных свойств.

Эти четыре новые функции включают в себя:

  • Возможность анимировать display и content-visibility на временной шкале ключевых кадров (из Chrome 116).
  • Свойство transition-behavior с ключевым словом allow-discrete , позволяющее включать переходы дискретных свойств, таких как display (из Chrome 117).
  • Правило @starting-style для анимации эффектов входа из display: none и в верхний слой (из Chrome 117).
  • Свойство overlay для управления поведением верхнего слоя во время анимации (из Chrome 117). ## Отображать анимацию в ключевых кадрах

Начиная с Chrome 116, вы можете использовать display и content-visibility в правилах ключевых кадров. Затем они поменяются местами в момент появления ключевого кадра. Для поддержки этого не требуется никаких дополнительных новых значений:

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

@keyframes fade-out {
  100% {
    opacity: 0;
    display: none;
  }
}

В предыдущем примере непрозрачность анимируется до 0 в течение 0,5 с, а затем устанавливается значение «нет». Кроме того, ключевое forwards гарантирует, что анимация останется в конечном состоянии, так что элемент, к которому она применяется, остается display: none и opacity: 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-behavior принимает два значения: normal и allow-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 также устанавливает это значение, поэтому вы можете опустить это свойство и вместо этого использовать ключевое allow-discrete в конце сокращенного обозначения transition для каждого перехода.

.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 , чтобы применить стиль, который браузер сможет найти до того, как элемент будет открыт на странице. Это состояние «до открытия» (из которого вы выполняете анимацию).

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

/*  1. 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;
}

/*  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:

Анимация элементов в верхнем слое и обратно

Чтобы анимировать элементы в верхнем слое и обратно, укажите @starting-style в «открытом» состоянии, чтобы сообщить браузеру, откуда начинать анимацию. Открытое состояние диалога определяется атрибутом [open] . Для поповера используйте псевдокласс :popover-open .

Простой пример диалога может выглядеть так:

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

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

/*   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 для большей визуальной инкапсуляции.

При анимации всплывающего окна используйте псевдокласс :popover-open вместо использованного ранее атрибута open .

.settings-popover {
  &:popover-open {
    /*  0. 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;
    }
    
    /*  1. IS-OPEN STATE  */
    /*  state when popover is open, BOTH:
        what we're transitioning *in* to 
        and transitioning *out* from */
    transform: translateY(0);
    opacity: 1;
  }
  
  /*  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 в список переходов. popover и dialog окна экранируют клипы-предки и преобразуют их, а также помещают содержимое в верхний слой. Если вы не overlay переход, ваш элемент немедленно снова станет обрезанным, преобразованным и закрытым, и вы не увидите, как произойдет переход.

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

Вместо этого включите 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 для каждой созданной карты.

Заключение

Эти новые функции платформы приближают нас на один шаг к плавной анимации входа и выхода на веб-платформе. Чтобы узнать больше, перейдите по этим ссылкам: