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