Pular para o conteúdo

A primeira escola especializada em tecnologia do Alto Solimões, com corpo técnico formado na área.

Módulo 4: Estado e movimento

Transição e animação sem exagero

Movimento que ajuda, e movimento que atrapalha

Transição

.acao {
background: var(--realce);
transition: filter 200ms ease;
}

.acao:hover {
filter: brightness(1.1);
}

A transition vai no estado normal, não no :hover. Assim ela vale nos dois sentidos: entrando e saindo. Declarada só no :hover, a entrada é suave e a volta é seca.

transition: propriedade duração curva atraso;
transition: opacity 200ms ease-in-out 0ms;

Várias, separadas por vírgula:

transition:
opacity 200ms ease,
transform 200ms ease;

Não use transition: all

/* Ruim */
transition: all 200ms;

/* Bom */
transition:
border-color 240ms ease,
transform 200ms ease;

all anima tudo: inclusive propriedades que você mudou por outro motivo, e inclusive as caras de animar. Listar o que se quer é mais previsível e mais rápido.

O que é barato animar

O navegador desenha um quadro por vez. Algumas propriedades o obrigam a recalcular o layout inteiro a cada quadro; outras ele entrega direto à placa de vídeo.

BaratoCaro
transformwidth, height
opacitymargin, padding
filtertop, left, right, bottom
/* Caro: recalcula o layout a cada quadro */
.cartao:hover {
margin-top: -4px;
}

/* Barato: a placa de vídeo resolve */
.cartao:hover {
transform: translateY(-4px);
}

O efeito visual é o mesmo. Num aparelho simples, a diferença entre um movimento liso e um movimento aos trancos - e o portfólio vai ser aberto em aparelhos simples.

A curva importa mais que a duração

transition: transform 240ms cubic-bezier(0.32, 0.72, 0, 1);

ease, ease-out e linear são atalhos para curvas prontas. Uma curva escrita à mão como a de cima - rápida no começo, desacelerando no fim - é o que faz um movimento parecer "caro" sem durar mais. Vale guardá-la numa variável:

:root {
--curva: cubic-bezier(0.32, 0.72, 0, 1);
--tempo: 240ms;
}

.cartao {
transition: border-color var(--tempo) var(--curva);
}

Animação com keyframes

@keyframes surgir {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

.abertura > * {
animation: surgir 500ms var(--curva) both;
}

Com etapas intermediárias:

@keyframes pulsar {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0;
transform: scale(2.2);
}
}

A bolinha do selo de disponibilidade

É o único movimento contínuo que um portfólio precisa - e é pequeno de propósito:

.selo-estado {
position: relative;
display: inline-flex;
width: 8px;
height: 8px;
}

.selo-estado::before {
content: "";
position: absolute;
inset: 0;
border-radius: 999px;
background: var(--realce);
animation: pulsar 2s ease-in-out infinite;
}

.selo-estado::after {
content: "";
position: absolute;
inset: 0;
border-radius: 999px;
background: var(--realce);
}

Duas camadas: uma parada e uma que cresce e some. É o efeito de "sinal ao vivo" que se vê ao lado de "Disponível para projetos".

E repare que a bolinha é ::before/::after - decoração pura, sem nenhum texto. A palavra "Disponível para projetos" está escrita ao lado, no HTML. Quem não vê a bolinha não perde nada.

A entrada da abertura, com escalonamento

.abertura > * {
animation: surgir 600ms var(--curva) both;
}

.abertura > *:nth-child(2) {
animation-delay: 80ms;
}
.abertura > *:nth-child(3) {
animation-delay: 160ms;
}
.abertura > *:nth-child(4) {
animation-delay: 240ms;
}

O selo entra, depois o nome, depois a função, depois os botões. É o efeito que faz uma abertura parecer trabalhada, e são doze linhas.

both no fim da declaração é o que impede o elemento de piscar: sem ele, o item com atraso aparece, some e reaparece.

Quanto tempo

DuraçãoPara quê
100–150msresposta a um toque
200–300msmudança de estado, abrir e fechar
300–600msentrada de conteúdo
acima dissoquase sempre demais

Quem não quer movimento

Movimento na tela causa enjoo, tontura e desconforto em muita gente: e o sistema operacional já oferece uma preferência para isso. Respeite-a:

@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

Este é um dos poucos lugares em que !important se justifica: a regra existe justamente para vencer qualquer animação declarada em qualquer lugar da folha.

O cartão com movimento contido

.cartao {
border: 1px solid var(--borda);
border-radius: var(--raio);
padding: var(--espaco-4);
transition:
transform var(--tempo) var(--curva),
border-color var(--tempo) var(--curva);
}

.cartao:hover {
transform: translateY(-2px);
border-color: var(--borda-forte);
}

Dois pixels. Não oito, não uma sombra colorida, não uma escala. Num portfólio de seis cartões, o efeito discreto repetido seis vezes lê como cuidado; o efeito exagerado repetido seis vezes lê como festa.

No portfólio

O movimento entra, e entra pouco:

  • --curva e --tempo no :root;
  • transição de transform e border-color nos cartões, com 2px de deslocamento;
  • transição de filter nos botões;
  • a bolinha pulsando no selo de disponibilidade - uma na página;
  • a entrada escalonada da abertura, com no máximo 300ms de atraso acumulado;
  • o bloco prefers-reduced-motion no fim da folha;
  • nenhum transition: all em lugar nenhum.

Depois ligue "reduzir movimento" nas configurações do sistema e recarregue. A página inteira deve ficar parada, e continuar perfeitamente utilizável.

Exercícios

  1. Ponha uma transição no botão e confira que ela funciona na ida e na volta. Depois mova a declaração para dentro do :hover e compare.
  2. Anime o cartão com translateY no hover e compare com margin-top, usando as ferramentas do navegador para medir os quadros.
  3. Escreva o @keyframes da bolinha e monte o selo de disponibilidade.
  4. Faça a entrada escalonada da abertura e cronometre o tempo total até tudo estar visível.
  5. Adicione o bloco de prefers-reduced-motion e teste ligando a preferência no sistema.
  6. Procure transition: all na sua folha e troque por uma lista explícita.

Referências

Testa o que você leu

6 perguntas sobre esta aula. Errar aqui não custa nada - a explicação vem junto da correção.

Pergunta 1 de 6
Onde se declara a `transition` para o movimento valer na ida e na volta?