Guias Detalhados
Animations

Animando sua Aplicação com CSS

CSS oferece um conjunto robusto de ferramentas para você criar animações bonitas e envolventes dentro de sua aplicação.

Como escrever animações em CSS nativo

Se você nunca escreveu animações CSS nativas, existem vários guias excelentes para começar. Aqui estão alguns deles: Guia de Animações CSS do MDN Guia de Animações CSS3 do W3Schools Tutorial Completo de Animações CSS Animação CSS para Iniciantes

e alguns vídeos: Aprenda Animação CSS em 9 Minutos Playlist de Tutorial de Animação CSS do Net Ninja

Confira alguns destes vários guias e tutoriais, e depois volte a este guia.

Criando Animações Reutilizáveis

Você pode criar animações reutilizáveis que podem ser compartilhadas em toda a sua aplicação usando @keyframes. Defina animações de keyframe em um arquivo CSS compartilhado, e você poderá reutilizar essas animações de keyframe onde quiser dentro de sua aplicação.

animations.css

@keyframes sharedAnimation {  to {    height: 0;    opacity: 1;    background-color: 'red';  }}.animated-class {  animation: sharedAnimation 1s;}.open {  height: '200px';  opacity: 1;  background-color: 'yellow';  transition: all 1s;}.closed {  height: '100px';  opacity: 0.8;  background-color: 'blue';  transition: all 1s;}.example-element {  animation-duration: 1s;  animation-delay: 500ms;  animation-timing-function: ease-in-out;}.example-shorthand {  animation: exampleAnimation 1s ease-in-out 500ms;}.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;}

Adicionar a classe animated-class a um elemento acionaria a animação nesse elemento.

Animando uma Transição

Animando Estado e Estilos

Você pode querer animar entre dois estados diferentes, por exemplo, quando um elemento é aberto ou fechado. Você pode fazer isso usando classes CSS, seja usando uma animação de keyframe ou estilização de transition.

animations.css

@keyframes sharedAnimation {  to {    height: 0;    opacity: 1;    background-color: 'red';  }}.animated-class {  animation: sharedAnimation 1s;}.open {  height: '200px';  opacity: 1;  background-color: 'yellow';  transition: all 1s;}.closed {  height: '100px';  opacity: 0.8;  background-color: 'blue';  transition: all 1s;}.example-element {  animation-duration: 1s;  animation-delay: 500ms;  animation-timing-function: ease-in-out;}.example-shorthand {  animation: exampleAnimation 1s ease-in-out 500ms;}.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;}

Acionar o estado open ou closed é feito alternando classes no elemento em seu component. Você pode encontrar exemplos de como fazer isso em nosso guia de template.

Você pode ver exemplos semelhantes no guia de template para animar estilos diretamente.

Transitions, Timing e Easing

Animar frequentemente requer ajustar timing, delays e comportamentos de easing. Isso pode ser feito usando várias propriedades CSS ou propriedades de atalho.

Especifique animation-duration, animation-delay e animation-timing-function para uma animação de keyframe em CSS, ou alternativamente use a propriedade de atalho animation.

animations.css

@keyframes sharedAnimation {  to {    height: 0;    opacity: 1;    background-color: 'red';  }}.animated-class {  animation: sharedAnimation 1s;}.open {  height: '200px';  opacity: 1;  background-color: 'yellow';  transition: all 1s;}.closed {  height: '100px';  opacity: 0.8;  background-color: 'blue';  transition: all 1s;}.example-element {  animation-duration: 1s;  animation-delay: 500ms;  animation-timing-function: ease-in-out;}.example-shorthand {  animation: exampleAnimation 1s ease-in-out 500ms;}.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;}

Da mesma forma, você pode usar transition-duration, transition-delay e transition-timing-function e o atalho transition para animações que não estão usando @keyframes.

animations.css

@keyframes sharedAnimation {  to {    height: 0;    opacity: 1;    background-color: 'red';  }}.animated-class {  animation: sharedAnimation 1s;}.open {  height: '200px';  opacity: 1;  background-color: 'yellow';  transition: all 1s;}.closed {  height: '100px';  opacity: 0.8;  background-color: 'blue';  transition: all 1s;}.example-element {  animation-duration: 1s;  animation-delay: 500ms;  animation-timing-function: ease-in-out;}.example-shorthand {  animation: exampleAnimation 1s ease-in-out 500ms;}.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;}

Acionando uma Animação

Animações podem ser acionadas alternando estilos ou classes CSS. Uma vez que uma classe está presente em um elemento, a animação ocorrerá. Remover a classe reverterá o elemento de volta para qualquer CSS que esteja definido para esse elemento. Aqui está um exemplo:

open-close.component.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-open-close',  templateUrl: 'open-close.component.html',  styleUrls: ['open-close.component.css'],})export class OpenCloseComponent {  isOpen = signal(true);  toggle() {    this.isOpen.update((isOpen) => !isOpen);  }}

open-close.component.html

<h2>Open / Close Example</h2><button type="button" (click)="toggle()">Toggle Open/Close</button><div class="open-close-container" [class.open]="isOpen()">  <p>The box is now {{ isOpen() ? 'Open' : 'Closed' }}!</p></div>

open-close.component.css

: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-color: blue;  color: #ebebeb;  transition-property: height, opacity, background-color, color;  transition-duration: 1s;}.open {  transition-duration: 0.5s;  height: 200px;  opacity: 1;  background-color: yellow;  color: #000000;}

Transition e Triggers

Animando Auto Height

Você pode usar css-grid para animar para altura automática.

auto-height.component.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-auto-height',  templateUrl: 'auto-height.component.html',  styleUrls: ['auto-height.component.css'],})export class AutoHeightComponent {  isOpen = signal(true);  toggle() {    this.isOpen.update((isOpen) => !isOpen);  }}

auto-height.component.html

<h2>Auto Height Example</h2><button type="button" (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>

auto-height.component.css

.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-color: blue;  color: #ebebeb;  overflow: hidden;}.container.open .content {  visibility: visible;}

Se você não precisa se preocupar em suportar todos os browsers, você também pode conferir calc-size(), que é a verdadeira solução para animar altura automática. Veja a documentação do MDN e este tutorial para mais informações.

Animar entrada e saída de uma view

Você pode criar animações para quando um item entra em uma view ou sai de uma view. Vamos começar olhando como animar um elemento entrando em uma view. Vamos fazer isso com animate.enter, que aplicará classes de animação quando um elemento entrar na view.

insert.component.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-insert',  templateUrl: 'insert.component.html',  styleUrls: ['insert.component.css'],})export class InsertComponent {  isShown = signal(false);  toggle() {    this.isShown.update((isShown) => !isShown);  }}

insert.component.html

<h2>Insert Element Example</h2><nav>  <button type="button" (click)="toggle()">Toggle Element</button></nav>@if (isShown()) {  <div class="insert-container" animate.enter="enter-animation">    <p>The box is inserted</p>  </div>}

insert.component.css

: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);  }}

Animar um elemento quando ele sai da view é semelhante a animar quando entra em uma view. Use animate.leave para especificar quais classes CSS aplicar quando o elemento sair da view.

remove.component.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-remove',  templateUrl: 'remove.component.html',  styleUrls: ['remove.component.css'],})export class RemoveComponent {  isShown = signal(false);  toggle() {    this.isShown.update((isShown) => !isShown);  }}

remove.component.html

<h2>Remove Element Example</h2><nav>  <button type="button" (click)="toggle()">Toggle Element</button></nav>@if (isShown()) {  <div class="insert-container" animate.leave="deleting">    <p>The box is inserted</p>  </div>}

remove.component.css

: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;}

Para mais informações sobre animate.enter e animate.leave, veja o guia de animações Enter e Leave.

Animando incremento e decremento

Animar no incremento e decremento é um padrão comum em aplicações. Aqui está um exemplo de como você pode realizar esse comportamento.

increment-decrement.component.ts

import {Component, ElementRef, OnInit, signal, viewChild} from '@angular/core';@Component({  selector: 'app-increment-decrement',  templateUrl: 'increment-decrement.component.html',  styleUrls: ['increment-decrement.component.css'],})export class IncrementDecrementComponent 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);  }}

increment-decrement.component.html

<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>

increment-decrement.component.css

: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);  }}

Desabilitando uma animação ou todas as animações

Se você quiser desabilitar as animações que especificou, você tem múltiplas opções.

  1. Crie uma classe personalizada que force animation e transition para none.
.no-animation {  animation: none !important;  transition: none !important;}

Aplicar esta classe a um elemento impede que qualquer animação seja disparada nesse elemento. Você poderia alternativamente delimitar isso ao seu DOM inteiro ou seção do seu DOM para aplicar esse comportamento. No entanto, isso impede que eventos de animação sejam disparados. Se você está aguardando eventos de animação para remoção de elemento, essa solução não funcionará. Uma solução alternativa é definir as durações para 1 milissegundo.

  1. Use a media query prefers-reduced-motion para garantir que nenhuma animação seja reproduzida para usuários que preferem menos animação.

  2. Previna adicionar classes de animação programaticamente

Callbacks de Animação

Se você tem ações que gostaria de executar em certos pontos durante as animações, há vários eventos disponíveis que você pode ouvir. Aqui estão alguns deles.

OnAnimationStart OnAnimationEnd OnAnimationIteration OnAnimationCancel

OnTransitionStart OnTransitionRun OnTransitionEnd OnTransitionCancel

A Web Animations API tem muita funcionalidade adicional. Dê uma olhada na documentação para ver todas as APIs de animação disponíveis.

NOTA: Esteja ciente de problemas de bubbling com esses callbacks. Se você está animando filhos e pais, os eventos sobem dos filhos para os pais. Considere parar a propagação ou olhar mais detalhes dentro do evento para determinar se você está respondendo ao evento alvo desejado em vez de um evento subindo de um nó filho. Você pode examinar a propriedade animationname ou as propriedades sendo transicionadas para verificar se você tem os nós certos.

Sequências Complexas

Animações são frequentemente mais complicadas do que apenas um simples fade in ou fade out. Você pode ter muitas sequências complicadas de animações que pode querer executar. Vamos dar uma olhada em alguns desses cenários possíveis.

Escalonando animações em uma lista

Um efeito comum é escalonar as animações de cada item em uma lista para criar um efeito cascata. Isso pode ser realizado utilizando animation-delay ou transition-delay. Aqui está um exemplo de como esse CSS pode parecer.

stagger.component.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-stagger',  templateUrl: './stagger.component.html',  styleUrls: ['stagger.component.css'],})export class StaggerComponent {  show = signal(true);  items = [1, 2, 3];  refresh() {    this.show.set(false);    setTimeout(() => {      this.show.set(true);    }, 10);  }}

stagger.component.html

<h1>Stagger Example</h1><button type="button" (click)="refresh()">Refresh</button>@if (show()) {  <ul class="items">    @for(item of items; track item) {      <li class="item" style="--index: {{ item }}">{{item}}</li>    }  </ul>}

stagger.component.css

.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);  }}

Animações Paralelas

Você pode aplicar múltiplas animações a um elemento de uma vez usando a propriedade de atalho animation. Cada uma pode ter suas próprias durações e delays. Isso permite que você componha animações juntas e crie efeitos complicados.

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

Neste exemplo, as animações rotate e fade-in disparam ao mesmo tempo, mas têm durações diferentes.

Animando os itens de uma lista reordenada

Itens em um loop @for serão removidos e readicionados, o que disparará animações usando @starting-styles para animações de entrada. Alternativamente, você pode usar animate.enter para esse mesmo comportamento. Use animate.leave para animar elementos à medida que eles são removidos, como visto no exemplo abaixo.

reorder.component.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-reorder',  templateUrl: './reorder.component.html',  styleUrls: ['reorder.component.css'],})export class ReorderComponent {  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;  }}

reorder.component.html

<h1>Reordering List Example</h1><button type="button" (click)="randomize()">Randomize</button><ul class="items">  @for(item of items; track item) {    <li class="item" animate.leave="fade">{{ item }}</li>  }</ul>

reorder.component.css

.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;  }}

Controle programático de animações

Você pode recuperar animações de um elemento diretamente usando Element.getAnimations(). Isso retorna um array de cada Animation naquele elemento. Você pode usar a API Animation para fazer muito mais do que poderia com o que o AnimationPlayer do pacote de animations oferecia. A partir daqui você pode cancel(), play(), pause(), reverse() e muito mais. Esta API nativa deve fornecer tudo que você precisa para controlar suas animações.

Mais sobre animações Angular

Você também pode estar interessado no seguinte: