Spec-Zone.ru › Angular

Анимация приложения с помощью CSS

CSS предлагает широкий набор инструментов для создания красивых и привлекательных анимаций в приложении.

Как создавать анимации на чистом CSS

Если вы никогда не создавали анимации на чистом CSS, есть множество отличных руководств, которые помогут вам начать. Вот несколько из них: Руководство MDN по CSS-анимациям Руководство W3Schools по CSS3-анимациям Полное руководство по CSS-анимациям CSS-анимация для начинающих

а также пара видео: Изучите CSS-анимацию за 9 минут Подборка руководств по CSS-анимации от Net Ninja

Ознакомьтесь с некоторыми из этих руководств и обучающих материалов, а затем вернитесь к этому руководству.

Создание повторно используемых анимаций

Вы можете создавать повторно используемые анимации, доступные во всём приложении, используя @keyframes. Определите ключевые кадры анимации в общем CSS-файле, и вы сможете повторно использовать эти анимации с ключевыми кадрами в любом месте приложения.

@keyframes sharedAnimation {
  to {
    height: 0;
    opacity: 1;
    background-color: 'red';
  }
}

.animated-class {
  animation: sharedAnimation 1s;
}

Добавление класса animated-class к элементу запускает анимацию этого элемента.

Анимация перехода

Анимация состояния и стилей

Вам может потребоваться анимировать переход между двумя разными состояниями, например, когда элемент открывается или закрывается. Это можно сделать с помощью CSS-классов, используя анимацию с ключевыми кадрами или стили перехода.

.open {
  height: '200px';
  opacity: 1;
  background-color: 'yellow';
  transition: all 1s;
}

.closed {
  height: '100px';
  opacity: 0.8;
  background-color: 'blue';
  transition: all 1s;
}

Переключение в состояние open или closed выполняется изменением классов элемента в компоненте. Примеры того, как это сделать, можно найти в нашем руководстве по шаблонам.

Похожие примеры можно найти в руководстве по шаблонам в разделе непосредственная анимация стилей.

Переходы, длительность и плавность

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

Для анимации с ключевыми кадрами в CSS укажите animation-duration, animation-delay и animation-timing-function или воспользуйтесь сокращённым свойством animation.

.example-element {
  animation-duration: 1s;
  animation-delay: 500ms;
  animation-timing-function: ease-in-out;
}

.example-shorthand {
  animation: exampleAnimation 1s ease-in-out 500ms;
}

Аналогично, для анимации без @keyframes можно использовать transition-duration, transition-delay и transition-timing-function, а также сокращённое свойство transition.

.example-element {
  transition-duration: 1s;
  transition-delay: 500ms;
  transition-timing-function: ease-in-out;
  transition-property: margin-right;
}

.example-shorthand {
  transition: margin-right 1s ease-in-out 500ms;
}

Запуск анимации

Анимацию можно запустить, переключая CSS-стили или классы. Когда класс добавлен к элементу, запускается анимация. Удаление класса возвращает элемент к стилям CSS, определённым для него. Вот пример:

import {Component, signal} from '@angular/core';

@Component({
  selector: 'app-open-close',
  templateUrl: 'open-close.html',
  styleUrls: ['open-close.css'],
})
export class OpenClose {
  isOpen = signal(true);
  toggle() {
    this.isOpen.update((isOpen) => !isOpen);
  }
}
<h2>Open / Close Example</h2>

<button type="button" class="toggle-btn" (click)="toggle()">Toggle Open/Close</button>

<div class="open-close-container" [class.open]="isOpen()">
  <p>The box is now {{ isOpen() ? 'Open' : 'Closed' }}!</p>
</div>
:host {
  display: block;
  margin-top: 1rem;
}

.open-close-container {
  border: 1px solid #dddddd;
  margin-top: 1em;
  padding: 20px 20px 0px 20px;
  font-weight: bold;
  font-size: 20px;
  height: 100px;
  opacity: 0.8;
  background: #3b82f6;
  color: #ebebeb;
  transition-property: height, opacity, background-color, color;
  transition-duration: 1s;
}

.toggle-btn {
  background: transparent;
  border: 1px solid var(--primary-contrast, black);
  color: var(--primary-contrast, black);
  padding: 10px 24px;
  border-radius: 8px;
  cursor: pointer;
}

.open {
  transition-duration: 0.5s;
  height: 200px;
  opacity: 1;
  background: #475569;
  color: #f9fafb;
}

Переходы и триггеры

Анимация автоматической высоты

Для анимации автоматической высоты можно использовать CSS Grid.

import {Component, signal} from '@angular/core';

@Component({
  selector: 'app-auto-height',
  templateUrl: 'auto-height.html',
  styleUrls: ['auto-height.css'],
})
export class AutoHeight {
  isOpen = signal(true);
  toggle() {
    this.isOpen.update((isOpen) => !isOpen);
  }
}
<h2>Auto Height Example</h2>

<button type="button" class="toggle-btn" (click)="toggle()">Toggle Open/Close</button>

<div class="container" [class.open]="isOpen()">
  <div class="content">
    <p>The box is now {{ isOpen() ? 'Open' : 'Closed' }}!</p>
  </div>
</div>
.container {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  transition: grid-template-rows 1s;
}

.container.open {
  grid-template-rows: 1fr;
}

.container .content {
  min-height: 0;
  transition: visibility 1s;
  padding: 0 20px;
  visibility: hidden;
  margin-top: 1em;
  font-weight: bold;
  font-size: 20px;
  background: #3b82f6;
  color: #ebebeb;
  overflow: hidden;
}

.container.open .content {
  visibility: visible;
}

.toggle-btn {
  background: transparent;
  border: 1px solid var(--primary-contrast, black);
  color: var(--primary-contrast, black);
  padding: 10px 24px;
  border-radius: 8px;
  cursor: pointer;
}

Если вам не нужно обеспечивать поддержку всех браузеров, обратите внимание на calc-size() — это настоящее решение для анимации автоматической высоты. Дополнительную информацию см. в документации MDN и этом руководстве.

Анимация появления и исчезновения элемента в представлении

Вы можете создавать анимации для появления элемента в представлении или его исчезновения. Начнём с того, как анимировать появление элемента. Для этого мы используем animate.enter, который применяет классы анимации при появлении элемента в представлении.

import {Component, signal} from '@angular/core';

@Component({
  selector: 'app-insert',
  templateUrl: 'insert.html',
  styleUrls: ['insert.css'],
})
export class Insert {
  isShown = signal(false);

  toggle() {
    this.isShown.update((isShown) => !isShown);
  }
}
<h2>Insert Element Example</h2>

<nav>
  <button type="button" class="toggle-btn" (click)="toggle()">Toggle Element</button>
</nav>

@if (isShown()) {
  <div class="insert-container" animate.enter="enter-animation">
    <p>The box is inserted</p>
  </div>
}
:host {
  display: block;
}

.insert-container {
  border: 1px solid #dddddd;
  margin-top: 1em;
  padding: 20px 20px 0px 20px;
  font-weight: bold;
  font-size: 20px;
}

.enter-animation {
  animation: slide-fade 1s;
}

@keyframes slide-fade {
  from {
    opacity: 0;
    transform: translateY(20px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.toggle-btn {
  background: transparent;
  border: 1px solid var(--primary-contrast, black);
  color: var(--primary-contrast, black);
  padding: 10px 24px;
  border-radius: 8px;
  cursor: pointer;
}

Анимация исчезновения элемента из представления похожа на анимацию его появления. Используйте animate.leave, чтобы указать, какие классы CSS применять при исчезновении элемента из представления.

import {Component, signal} from '@angular/core';

@Component({
  selector: 'app-remove',
  templateUrl: 'remove.html',
  styleUrls: ['remove.css'],
})
export class Remove {
  isShown = signal(false);

  toggle() {
    this.isShown.update((isShown) => !isShown);
  }
}
<h2>Remove Element Example</h2>

<nav>
  <button type="button" class="toggle-btn" (click)="toggle()">Toggle Element</button>
</nav>

@if (isShown()) {
  <div class="insert-container" animate.leave="deleting">
    <p>The box is inserted</p>
  </div>
}
:host {
  display: block;
}

.insert-container {
  border: 1px solid #dddddd;
  margin-top: 1em;
  padding: 20px 20px 0px 20px;
  font-weight: bold;
  font-size: 20px;
  opacity: 1;
  transition: opacity 200ms ease-in;

  @starting-style {
    opacity: 0;
  }
}

.deleting {
  opacity: 0;
  transform: translateY(20px);
  transition:
    opacity 500ms ease-out,
    transform 500ms ease-out;
}

.toggle-btn {
  background: transparent;
  border: 1px solid var(--primary-contrast, black);
  color: var(--primary-contrast, black);
  padding: 10px 24px;
  border-radius: 8px;
  cursor: pointer;
}

Дополнительную информацию о animate.enter и animate.leave см. в руководстве по анимации появления и исчезновения.

Анимация увеличения и уменьшения

Анимация при увеличении и уменьшении — распространённый шаблон в приложениях. Вот пример того, как реализовать такое поведение.

import {Component, ElementRef, OnInit, signal, viewChild} from '@angular/core';

@Component({
  selector: 'app-increment-decrement',
  templateUrl: 'increment-decrement.html',
  styleUrls: ['increment-decrement.css'],
})
export class IncrementDecrement implements OnInit {
  num = signal(0);
  el = viewChild<ElementRef<HTMLParagraphElement>>('el');

  ngOnInit() {
    this.el()?.nativeElement.addEventListener('animationend', (ev) => {
      if (ev.animationName.endsWith('decrement') || ev.animationName.endsWith('increment')) {
        this.animationFinished();
      }
    });
  }

  modify(n: number) {
    const targetClass = n > 0 ? 'increment' : 'decrement';
    this.num.update((v) => (v += n));
    this.el()?.nativeElement.classList.add(targetClass);
  }

  animationFinished() {
    this.el()?.nativeElement.classList.remove('increment', 'decrement');
  }

  ngOnDestroy() {
    this.el()?.nativeElement.removeEventListener('animationend', this.animationFinished);
  }
}
<h3>Increment and Decrement Example</h3>
<section>
  <p #el>Number {{ num() }}</p>
  <div class="controls">
    <button type="button" (click)="modify(1)">+</button>
    <button type="button" (click)="modify(-1)">-</button>
  </div>
</section>
:host {
  display: block;
  font-size: 32px;
  margin: 20px;
  text-align: center;
}

section {
  border: 1px solid lightgray;
  border-radius: 50px;
}

p {
  display: inline-block;
  margin: 2rem 0;
  text-transform: uppercase;
}

.increment {
  animation: increment 300ms;
}

.decrement {
  animation: decrement 300ms;
}

.controls {
  padding-bottom: 2rem;
}

button {
  font: inherit;
  border: 0;
  background: lightgray;
  width: 50px;
  border-radius: 10px;
}

button + button {
  margin-left: 10px;
}

@keyframes increment {
  33% {
    color: green;
    transform: scale(1.3, 1.2);
  }
  66% {
    color: green;
    transform: scale(1.2, 1.2);
  }
  100% {
    transform: scale(1, 1);
  }
}

@keyframes decrement {
  33% {
    color: red;
    transform: scale(0.8, 0.9);
  }
  66% {
    color: red;
    transform: scale(0.9, 0.9);
  }
  100% {
    transform: scale(1, 1);
  }
}

Отключение одной или всех анимаций

Если вы хотите отключить заданные анимации, у вас есть несколько вариантов.

  1. Создайте пользовательский класс, который принудительно задаёт для анимации и перехода значение none.
.no-animation {
  animation: none !important;
  transition: none !important;
}

Применение этого класса к элементу предотвращает запуск любых анимаций для него. Вместо этого можно задать область действия класса для всего DOM или его части, чтобы обеспечить такое поведение. Однако это не позволяет запускать события анимации. Если вы ожидаете события анимации перед удалением элемента, это решение не подойдёт. В качестве обходного решения можно задать длительность в 1 миллисекунду.

  1. Используйте медиазапрос prefers-reduced-motion, чтобы анимации не запускались у пользователей, которые предпочитают уменьшить их количество.

  2. Не добавляйте классы анимации программно

Обратные вызовы анимации

Если вы хотите выполнять действия в определённые моменты анимации, можно подписаться на ряд доступных событий. Вот некоторые из них.

OnAnimationStart
OnAnimationEnd
OnAnimationIteration
OnAnimationCancel

OnTransitionStart
OnTransitionRun
OnTransitionEnd
OnTransitionCancel

Web Animations API предоставляет множество дополнительных возможностей. Ознакомьтесь с документацией, чтобы узнать обо всех доступных API для анимации.

ПРИМЕЧАНИЕ: Помните о проблемах всплытия событий при использовании этих обратных вызовов. Если вы анимируете дочерние и родительские элементы, события всплывают от дочерних элементов к родительским. Чтобы определить, обрабатываете ли вы событие именно для нужной цели, а не событие, всплывшее от дочернего узла, остановите распространение события или изучите его подробнее. Убедиться, что выбраны нужные узлы, можно по свойству animationname или свойствам, для которых выполняется переход.

Сложные последовательности

Анимации часто бывают сложнее простого появления или исчезновения. Возможно, вам понадобится запускать множество сложных последовательностей анимации. Рассмотрим несколько возможных сценариев.

Последовательный запуск анимаций в списке

Распространённый эффект — запускать анимацию каждого элемента списка с небольшой задержкой, создавая каскадный эффект. Этого можно добиться с помощью animation-delay или transition-delay. Вот как может выглядеть соответствующий CSS.

import {Component, signal} from '@angular/core';

@Component({
  selector: 'app-stagger',
  templateUrl: './stagger.html',
  styleUrls: ['stagger.css'],
})
export class Stagger {
  show = signal(true);
  items = [1, 2, 3];

  refresh() {
    this.show.set(false);
    setTimeout(() => {
      this.show.set(true);
    }, 10);
  }
}
<h1>Stagger Example</h1>
<button type="button" class="toggle-btn" (click)="refresh()">Refresh</button>
@if (show()) {
  <ul class="items">
    @for (item of items; track item) {
      <li class="item" style="--index: {{ item }}">{{ item }}</li>
    }
  </ul>
}
.items {
  list-style: none;
  padding: 0;
  margin: 0;
}

.items .item {
  transition-property: opacity, transform;
  transition-duration: 500ms;
  transition-delay: calc(200ms * var(--index));

  @starting-style {
    opacity: 0;
    transform: translateX(-10px);
  }
}

.toggle-btn {
  background: transparent;
  border: 1px solid var(--primary-contrast, black);
  color: var(--primary-contrast, black);
  padding: 10px 24px;
  border-radius: 8px;
  cursor: pointer;
}

Параллельные анимации

С помощью сокращённого свойства animation можно одновременно применить к элементу несколько анимаций. Для каждой из них можно задать собственную длительность и задержку. Это позволяет комбинировать анимации и создавать сложные эффекты.

.target-element {
  animation:
    rotate 3s,
    fade-in 2s;
}

В этом примере анимации rotate и fade-in запускаются одновременно, но имеют разную длительность.

Анимация элементов списка при изменении порядка

Элементы в цикле @for будут удалены и добавлены снова, что запустит анимации появления с помощью @starting-styles. Для такого же поведения можно использовать animate.enter. Используйте animate.leave, чтобы анимировать удаление элементов, как показано в примере ниже.

import {Component, signal} from '@angular/core';

@Component({
  selector: 'app-reorder',
  templateUrl: './reorder.html',
  styleUrls: ['reorder.css'],
})
export class Reorder {
  show = signal(true);
  items = ['stuff', 'things', 'cheese', 'paper', 'scissors', 'rock'];

  randomize() {
    const randItems = [...this.items];
    const newItems = [];
    for (let i of this.items) {
      const max: number = this.items.length - newItems.length;
      const randNum = Math.floor(Math.random() * max);
      newItems.push(...randItems.splice(randNum, 1));
    }

    this.items = newItems;
  }
}
<h1>Reordering List Example</h1>
<button type="button" class="toggle-btn" (click)="randomize()">Randomize</button>

<ul class="items">
  @for (item of items; track item) {
    <li class="item" animate.leave="fade">{{ item }}</li>
  }
</ul>
.items {
  list-style: none;
  padding: 0;
  margin: 0;
}

.items .item {
  transition-property: opacity, transform;
  transition-duration: 500ms;

  @starting-style {
    opacity: 0;
    transform: translateX(-10px);
  }
}

.items .item.fade {
  animation: fade-out 500ms;
}

@keyframes fade-out {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

.toggle-btn {
  background: transparent;
  border: 1px solid var(--primary-contrast, black);
  color: var(--primary-contrast, black);
  padding: 10px 24px;
  border-radius: 8px;
  cursor: pointer;
}

Программное управление анимациями

Получить анимации элемента можно напрямую с помощью Element.getAnimations(). Этот метод возвращает массив всех объектов Animation этого элемента. С помощью API Animation можно сделать гораздо больше, чем позволял AnimationPlayer из пакета анимаций. С его помощью можно cancel(), play(), pause(), reverse() и многое другое. Этот встроенный API должен предоставить всё необходимое для управления анимациями.

Подробнее об анимациях Angular

Возможно, вас также заинтересуют следующие материалы:

Анимация появления и исчезновения Анимация переходов между маршрутами

Super-powered by Google ©2010–2025.
Code licensed under an MIT-style License. Documentation licensed under CC BY 4.0.
https://angular.dev/guide/animations/css

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API