Curso Animações Web
ms

Easing e Bézier curve com CSS

O que é easing

Easing é a curva que define como o progresso de uma animação ocorre ao longo do tempo. Duas animações com a mesma distância e a mesma duração podem parecer completamente diferentes, porque uma acelera no começo e freia no final, enquanto a outra anda sempre no mesmo ritmo. No CSS, essa curva vai em transition-timing-function ou animation-timing-function, e quando você não define nenhuma o navegador usa ease por padrão. É ela que separa um movimento que parece de máquina de um que parece natural.

Para ler uma curva de easing, pense num gráfico em que o eixo horizontal é o tempo e o vertical é o progresso. Onde a curva sobe rápido o elemento anda rápido, e onde ela fica quase deitada ele está freando. É por isso que a curva de um ease-out começa inclinada e termina deitada, porque o elemento sai rápido e chega devagar.

cubic-bezier()

As palavras-chave ease, ease-in, ease-out e ease-in-out não têm nada de mágico, cada uma é uma curva de Bézier cúbica com valores fixos. Com a função cubic-bezier(x1, y1, x2, y2) você escolhe esses valores, que são os dois pontos de controle de uma curva que sempre começa em (0, 0) e termina em (1, 1). O x é o tempo e precisa ficar entre 0 e 1. O y é o progresso e pode passar desses limites, e é isso que faz o elemento ultrapassar o destino e voltar, o efeito de back (ou overshoot). Com um y negativo acontece o contrário, e o elemento recua um pouco antes de sair.

As palavras-chave e a cubic-bezier() equivalente
Palavra-chave Equivale a Quando usar
linear cubic-bezier(0, 0, 1, 1) giros contínuos, como um spinner de loading
ease cubic-bezier(0.25, 0.1, 0.25, 1) o padrão, quando nenhum easing é definido
ease-in cubic-bezier(0.42, 0, 1, 1) elementos que saem da tela
ease-out cubic-bezier(0, 0, 0.58, 1) elementos que entram na tela
ease-in-out cubic-bezier(0.42, 0, 0.58, 1) um elemento que vai de um ponto a outro

linear() e animações spring no CSS

Uma cubic-bezier passa do destino no máximo uma vez, e uma mola vai, volta e vai de novo até parar. Para isso o CSS ganhou a função linear(), que recebe uma lista de pontos e liga cada um ao próximo com uma reta. Parece limitado, mas com pontos suficientes essas retas desenham qualquer curva, incluindo spring, bounce e elastic.

/* sobe até 1.2 aos 60% do tempo, volta para 0.9 aos 80% e termina em 1 */
transition-timing-function: linear(0, 1.2 60%, 0.9 80%, 1);

Quando um ponto vem sem porcentagem, o CSS distribui os pontos em intervalos iguais ao longo da duração. Uma mola convincente precisa de dezenas de pontos, e ninguém escreve isso na mão, por isso eles são gerados a partir da física da mola e colados no CSS. Quanto mais pontos, mais lisa fica a curva. Com poucos, as retas aparecem e o movimento fica quebrado.

steps() e animações em degraus

Nem toda animação precisa ser suave. A função steps() divide a duração em intervalos iguais e faz o valor pular de um para o outro, sem nada no meio, como o ponteiro de segundos de um relógio. É o que você usa numa animação de sprite, em que cada quadro da imagem aparece de uma vez.

O número é a quantidade de intervalos. O segundo parâmetro é opcional e diz quando o valor pula: com jump-end (o padrão, que também pode ser escrito end) ele pula no fim de cada intervalo, e com jump-start (ou start) no começo. Existe ainda o jump-none, que mostra o valor inicial e o final como degraus inteiros, e o jump-both, que pula tanto no começo quanto no fim.

/* ponteiro de segundos: 60 degraus em 60 segundos */
.ponteiro {
  animation: girar 60s steps(60) infinite;
}

Dá para montar o mesmo degrau com linear(). Quando um ponto recebe duas porcentagens, ele segura o valor entre elas, e a ligação até o próximo ponto vira um pulo. O código abaixo faz o mesmo que steps(4, jump-none).

/* segura 0 até 25%, 0.333 até 50%, 0.667 até 75% e termina em 1 */
animation-timing-function: linear(0 0% 25%, 0.333 25% 50%, 0.667 50% 75%, 1 75%);

Como usar o easing no seu CSS

Qualquer um desses valores vai no lugar do easing, tanto na transition quanto na animation, seja na propriedade abreviada ou em transition-timing-function e animation-timing-function.

.card {
  transition: translate 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.toast {
  animation: entrar 0.6s linear(0, 0.365, 0.829, 1.043, 1.071, 1.035, 1.006, 0.995, 1);
}

Um detalhe que pega muita gente na animation: o easing vale para cada intervalo entre dois keyframes, e não para a animação inteira. Com keyframes em 0%, 50% e 100%, a curva roda duas vezes, uma em cada metade. Se você quer uma curva só do começo ao fim, use só o from e o to.

Perguntas frequentes

Qual easing eu uso na maioria das animações de interface?

Para elementos que entram na tela, eu usaria um ease-out ou uma versão mais forte dele, como cubic-bezier(0.22, 1, 0.36, 1). Ele começa rápido, o que dá a sensação de resposta imediata, e freia devagar no final. Para elementos que saem, ease-in, e para giros contínuos, linear.

Dá para fazer uma animação spring só com CSS?

Dá, com linear(). O que o CSS não faz é a física de verdade, então a mola sempre termina junto com a duration, mesmo que uma mola real ainda estivesse balançando. Eu diria que, com uma duration bem escolhida, quase ninguém percebe a diferença.

A função linear() funciona em todos os navegadores?

Funciona. Segundo o MDN, linear() está disponível em todos os navegadores principais desde dezembro de 2023.

Qual a diferença entre ease-in, ease-out e ease-in-out?

O ease-in começa devagar e termina rápido, por isso combina com elementos que saem da tela. O ease-out faz o contrário, começa rápido e termina devagar, e combina com elementos que entram. O ease-in-out é devagar nas duas pontas, bom para um elemento que vai de um ponto a outro da tela.