Entre para a LISTA VIP da Black Friday

00

DIAS

00

HORAS

00

MIN

00

SEG

Clique para saber mais
Alura > Cursos de Front-end > Cursos de Angular > Conteúdos de Angular > Primeiras aulas do curso Angular: aprimore suas técnicas de animação e crie interfaces ainda mais atraentes

Angular: aprimore suas técnicas de animação e crie interfaces ainda mais atraentes

Busca animada com keyframes, offset e cubic-bezier - Apresentação

Olá, eu sou a Nay! Boas-vindas ao curso de Angular: aprimore suas técnicas de animação!

Nayanne Batista é uma pessoa de pele morena. Tem olhos castanhos escuros e cabelos lisos também castanhos escuros. Usa óculos de grau com armação quadrada e está com uma camiseta laranja. Ao fundo, uma estante com livros e iluminação na cor verde.

Sabia que as pessoas usuárias formam uma primeira opinião sobre seu site em apenas 50 milissegundos?

É por isso que causar uma boa impressa é fundamental!

Quem contribui para isso são as animações. Além de deixarem o visual do site mais bonito, também melhoram a usabilidade por meio de feedbacks animados durante a navegação.

Esse curso é para você que já possui conhecimento sobre animação e também a biblioteca RxJS

Nas aulas, vamos explorar métodos mais avançados de animações, como group(), query(), stagger(), keyframes() e cubic-bezier(). Assim descobriremos como criar animações mais complexas e personalizadas.

Durante o curso, você irá aprender:

Usaremos o RxJS para as animações funcionarem de forma eficiente.

Aceita o desafio? Te esperamos no próximo vídeo!

Busca animada com keyframes, offset e cubic-bezier - Buscando tarefas

Que bom que você está de volta! Nesse curso, continuaremos animando o projeto Memorando.

A instrutora conta que está utilizando o projeto para cadastrar suas tarefas pessoais de trabalho e estudo, por isso o projeto está com uma lista maior.

O nível de complexidade dessas tarefas variam, as mais dfíceis, por exemplo, demandam mais tempo para serem realizadas.

Como a instrutora finalizou a tarefa chamada "Refatorar Código" ela a procura no projeto, utilizando o scroll, e a marca como cumprida, sinalizando com o "check".

Como a lista é grande, não é tão simples encontrar uma tarefa entre as demais cadastradas. Uma solução que simplificaria esse processo seria a implementação de um campo para filtrar as tarefas.

Essa é a missão dessa aula! Construiremos essa solução e deixaremos todo o processo animado.

Buscando tarefas

Começamos abrindo o VS Code. Como a aplicação já está sendo executada, ao clicarmos na pasta "app > lista-tarefas" encontramos os arquivos lista-tarefas.component.html e lista-tarefas.component.ts, abrimos os dois.

lista-tarefas.component.html

Abaixo da imagem, img src, vamos inserir um campo de busca. Para isso, criamos uma div.class="busca".

Nela, criamos um parágrafo <p></p> com o atributo de classe"ff-prompt" e o texto Procurando o que fazer?.

<! -- código omitido -->

  <div class="busca">
    <p class="ff-prompt">Procurando o que fazer?</p>

Na linha abaixo, criamos um input. Ele será type="search" e terá o id "campo-busca". Na linha seguinte criamos o atributo placeholder="Busque por uma tarefa".

<! -- código omitido -->

    <input
      id="campo-busca" type="search"
      placeholder="Busque por uma tarefa"

Feito isso, abrimos nossa aplicação. Repare que, no canto esquerdo da tela, encontramos a div com o parágrafo e o input. Eles estão simples, pois ainda não criamos os estilos. Faremos isso agora.

lista-tarefas.component.css

Clicamos no menu lateral e abrimos o arquivo lista-tarefas.component.css. Nele, codamos todos os estilos necessários. Então, vamos descomentar da linha 1 até a 55, assim você poderá ter acesso a esses estilos também.

Não faremos um passo a passo, pois esse não é o foco do curso. Mas, caso você tenha interesse em aprender mais sobre esse tema, pode encontrar outros cursos na plataforma sobre css e responsividade.

Feito isso, fechamos o arquivo css. Ao voltar no Memorando, já temos a div centralizada e com o estilo que definimos. Porém ainda não está funcionando.

lista-tarefas.component.html

Voltamos no VS Code. Abaixo de placeholder, mais especificamente na linha 77, faremos uma vinculação bidirecional. Abrimos colchetes e parênteses e dentro escrevemos a diretiva ngModel. Nela, passamos o atributo "campoBusca".

<! -- código omitido -->

[(ngModel)]="campoBusca"

lista-tarefas.component.ts

Feito isso, copiamos esse atributo, abrimos o arquivo lista-tarefas.component.ts e colamos na linha 26, no fim de export class. Definimos o tipo string inicializando vazia, string = ''.

Embora essa tipagem seja opcional, optamos por defini-la pois fizemos isso nos demais atributos.

// código omitido

campoBusca: string = '';

lista-tarefas.component.html

Voltando ao template, criamos um keyup atribuindo a expressão filtrarTarefasPorDescricao() e passando como parâmetro campoBusca.

lista-tarefas.component.ts

Agora, criaremos esse componente. Para isso, copiamos o nome desse método. Depois, no arquivo lista-tarefas.component.ts, na linha 49, abaixo do ngOnInit(), o colamos.

Nos parênteses, passamos como parâmetro descricao e tipamos como string. Em seguida, abrimos o bloco com as chaves.

//código omitido

filtrarTarefasPorDescricao(descricao: string) {

}

Na linha 27, criamos outro atributo chamado tarefasFiltradas, que será o array resultante da busca. Ela será do tipo Tarefas[] = [].

//código omitido

tarefasFiltradas: Tarefas[] = [];

Voltamos no método filtrarTarefasPorDescricao() para fazer um tratamento para o campo de busca. Na linha abaixo escrevemos this.campoBusca = descricao.trim().toLowerCase(), assim removeremos o espaço e deixaremos o termo em letra minúscula para não haver divergências quando formos fazer a comparação.

// código omitido

  filtrarTarefasPorDescricao(descricao: string) {
    this.campoBusca = descricao.trim().toLowerCase();

Na linha abaixo, vamos codar a lógica. Então, digitamos if(descricao), ou seja, se a pessoa digitou no campo de busca, vamos popular o array de tarefas filtradas, this.tarefasFiltradas = this.listaTarefas.filter().

Dentro dos parênteses passamos tarefas, uma arrow function e escrevemos tarefa.descricao.ToLowerCase(). Em seguida, passamos o método includes() passando o this.campoBusca.

Na linha 55, criamos o else{}. Dentro das chaves, escrevemos this.tarefasFiltradas = this.listaTarefas.

// código omitido

  filtrarTarefasPorDescricao(descricao: string) {
    this.campoBusca = descricao.trim().toLowerCase();
    if (descricao) {
      this.tarefasFiltradas = this.listaTarefas.filter(tarefa =>
        tarefa.descricao.toLowerCase().includes(this.campoBusca));
    } else {
      this.tarefasFiltradas = this.listaTarefas;
    }
  }

Para funcionar, no trecho de código ngOnInit(), deixamos o cursor parado no fim da linha this.listaTarefas e apertamos as teclas "Alt + Shif + Seta para baixo". Nessa nova linha, atribuímos o array this.tarefasFiltradas para que receba a lista de tarefas.

Na linha 48, ao invés de retornar a listaTarefas, retornamos tarefasFiltradas. Dessa forma:

// código omitido

  ngOnInit(): Tarefa[] {
    this.service.listar(this.categoria).subscribe((listaTarefas) => {
      this.listaTarefas = listaTarefas;
      this.tarefasFiltradas = listaTarefas;
    });
    return this.tarefasFiltradas;
  }

Também precisamos fazer uma modificação no método listarAposCheck(). Na linha 141, substituimos para this.tarefasFiltradas = listaTarefas.

// código omitido

listarAposCheck() {
    this.service.listar(this.categoria).subscribe((listaTarefas) => {
      this.tarefasFiltradas = listaTarefas;
    });
  }

lista-tarefas.component.html

Por fim, no arquivo de template, faremos a mesma substituição nas linhas 80 e 83, apagando listaTarefas e escrevendo tarefasFiltradas.

<! -- código omitido -->

<div *ngIf="tarefasFiltradas.length > 0, else semTarefas">
    <div
      class="lista-tarefas card-{{ tarefa.categoria }}"
      *ngFor="let tarefa of tarefasFiltradas; let i = index"

<! -- código omitido -->

Vamos testar. Acessamos o Memorando e no campo de busca escrevemos "reunião". Repare que assim que começamos as tarefas começam a ser filtradas. Deu certo!

Agora precisamos animar esse processo. É isso que faremos no próximo vídeo.

Até lá!

Busca animada com keyframes, offset e cubic-bezier - Controlando com keyframes e offset

No vídeo anterior criamos um campo para filtrar as tarefas. Agora, tornaremos esse processo animado para que os cards apareçam e desapareçam gradualmente.

Controlando com keyframes() e offset

Para isso, abrimos o VS Code. Clicamos no menu lateral esquerdo, depois na pasta "service" e abrimos o arquivo animations.ts.

No fim do código, criamos a nova constante export const que se chamará filterTrigger.

Adicionamos um sinal de igual e vamos associá-la a um novo trigger(). Como parâmetro ele receberá 'filterAnimation' e um array de metadados [].

Como não queremos animar o estilo inicial e final dos cards, não utilizaremos state nem style e sim o transition().

Para animar a entrada e saída do card, nos parênteses passamos os parâmetros ':enter' e [].

// código omitido

export const filterTrigger = trigger('filterAnimation', [
  transition(':enter', [
    ])
])

Na linha abaixo, passamos o style(). Nos parênteses adicionamos chaves e dentro delas escrevemos opacity: 0, width: 0.

Adicionamos vírgula e na próxima linha adicionamos o método animate(). Nele, passamos como primeiro parâmetro a duração da animação '400ms'seguido da time function ease-out. O parâmetro seguinte será outra função style({}).

Nas chaves passamos opacity: 1e na próxima linha width: ''*.

Para definir a largura o ideal seria saber a medida final do elemento. Porém, nesses casos podemos utilizar o curinga.

Sim! Além de utilizá-lo como estado também podemos passá-lo como valor para uma propriedade css.

// código omitido

export const filterTrigger = trigger('filterAnimation', [
  transition(':enter', [
        style({opacity: 0, width: 0}),
        animate('400ms ease-out', style({
            opacity:1,
            width: '*'
        }))
    ])
])

Na linha 52, após ]), adicionamos uma vírgula. Damos espaço e na nova linha criaremos a transição de saída do elemento. Escrevemos transition(). Dentro passamos os parâmetros ':leave' e [].

Em seguida, passamos a função animate() com a mesma duração '400ms' e a time function ease-out. Adicionamos vírgula e a função style({opacity: 0, width:0}). Assim, criamos a animação.

// código omitido

  transition(':leave', [
    animate('200ms cubic-bezier(.13,.9,.8,.1)', style({ opacity: 0, width: 0}))
  ])
])

lista-tarefas.components.ts

Agora, faremos as importações necessárias. Em animations, após checkButtonTrigger, adicionamos vírgula. Na linha abaixo escrevemos filterTrigger. Feito isso ele também é importado na linha 7 do código.

// código omitido

import { checkButtonTrigger, filterTrigger, highlightedStateTrigger, shownStateTrigger } from '../animations';

@Component({
  selector: 'app-lista-tarefas',
  templateUrl: './lista-tarefas.component.html',
  styleUrls: ['./lista-tarefas.component.css'],
  animations: [
    highlightedStateTrigger,
    shownStateTrigger,
    checkButtonTrigger,
    filterTrigger
  ]
})
// código omitido

Para testar, acessamos o Memorando. Na barra de busca, escrevemos uma palavra. Notamos que a animação ainda não está funcionando. Para que isso aconteça, precismos a associação no template. Então, voltamos ao VS Code.

lista-tarefas-component.html

Na div da linha 81 adicionamos @filterAnimation, que é o nome do trigger.

<! -- código omitido -->

    <div
      @filterAnimation
      class="lista-tarefas card-{{ tarefa.categoria }}"
      *ngFor="let tarefa of tarefasFiltradas; let i = index"
      (mouseover)="indexTarefa = i"
      [@highlightedState]="indexTarefa === i ? 'highlighted' : 'default'">

Voltamos no Memorando para testar novamente. Ao digitar no campo de busca os cards vão aparecendo e desaparecendo gradualmente. Está funcionando!

animations.ts

Voltamos no arquivo animations.ts. Será que durante os 400ms conseguimos fazer uma modificação em diferentes etapas desse tempo?

A resposta é sim! Podemos utilizar o keyframes() para adicionar estilos intermediários na animação ao invés do style() final.

Então, na linha 48, em animate, apagamos o style() e suas propriedades. No lugar, adicionamos o keyframes().

Como precisamos importá-lo da biblioteca, com o mouse em cima dele, clicamos em "Quick Fix" e depois em "Update import from "angular/animations".

Em keyframes() passamos o array de estilos []. Agora, podemos passar os estilos intermediários.

Dentro das chaves escrevemos style({opacity: 0, width:0}). Com o cursor no fim dessa linha de código adicionamos vírgula e apertamos duas vezes as teclas "Alt + Shift + Seta para baixo". Assim, o trecho é replicado.

No segundo estilo, definimos a opacidade 0.5 e largura '*'. No terceiro estilo opacidade 1 e largura '*'.

// código omitido

    animate('400ms ease-out', keyframes([
      style({opacity:0, width:0}),
      style({opacity:0.5, width:'*'}),
      style({opacity:1, width:'*'})

Voltamos ao Memorando e no campo de busca, digitamos uma palavra. Conseguimos reparar uma pequena mudança.

Os keyframes() animam vários estilos intermediários. Porém, em animações mais curtas como a nossa não ficam tão perceptíveis.

Por isso, adicionaremos outra propriedade css. No segundo stylepassamos backgroundCollor: 'lightgreen'. Copiamos esse trecho e colamos no estilo final, mudando a cor para lightblue. Dessa forma:

// código omitido

    animate('400ms ease-out', keyframes([
      style({opacity:0, width:0}),
      style({opacity:0.5, width:'*', backgroundCollor: 'lightgreen'}),
      style({opacity:1, width:'*', backgroundCollor: 'lightblue'})

Ao testar no Memorando percebemos que quando os cards aparecem vão mudando de cor de verde para azul. Assim, animamos as etapas da animação.

Nesse momento, você pode estar se questionando se também é possível controlar o tempo em que essa etapa acontece. Sim! Podemos fazer isso utilizando o offset.

offset é uma propriedade que recebe um valor numérico entre 0 e 1. Assim, podemos controlar o momento exato em que a mudança da propriedade css acontece.

Na linha 49, no primeiro style() adicionamos o offset:0, no segundo offset: 0.8 e no último offset: 1.

//código omitido
export const filterTrigger = trigger('filterAnimation', [
  transition(':enter', [
    style({opacity: 0, width: 0}),
    animate('400ms ease-out', keyframes([
      style({offset: 0, opacity:0, width:0}),
      style({offset: 0.8, opacity:0.5, width:'*'}),
      style({offset: 1, opacity:1, width:'*'})
    ]))
  ]),
  transition(':leave', [
    animate('200ms cubic-bezier(.13,.9,.8,.1)', style({ opacity: 0, width: 0}))
  ])
])

Voltamos na aplicação para testar. Ao realizar a busca, percebemos que deu certo!

Para visualizar melhor, mudamos o tempo de animate para 2000ms. Ao fazer isso, percebemos todos os detalhes da animação e a mudança da cor verde para a azul.

Conclusão:

Os keyframes() são recursos muito importantes para criarmos animações no Angular. A partir deles, podemos inserir estilos intermediários nas animações, além de controlar o tempo em que as mudanças de propriedades css acontecerão.

No próximo vídeo conheceremos mais uma ferramenta que irá adicionar mais personalização no seu projeto.

Até lá!

Sobre o curso Angular: aprimore suas técnicas de animação e crie interfaces ainda mais atraentes

O curso Angular: aprimore suas técnicas de animação e crie interfaces ainda mais atraentes possui 102 minutos de vídeos, em um total de 42 atividades. Gostou? Conheça nossos outros cursos de Angular em Front-end, ou leia nossos artigos de Front-end.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Angular acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas