1 pontos por GN⁺ 2024-03-09 | 1 comentários | Compartilhar no WhatsApp

Truque de animação usando suavização exponencial

  • Há uma técnica de animação simples que uso quase sempre desde que comecei a trabalhar com animação.
  • Essa técnica é usada em vários lugares, como rotação e movimento de câmera, movimentação de personagens em jogos por turnos, deslocamento de elementos de UI e suavização de mudanças de volume em bibliotecas de áudio.
  • Essa técnica não é nova; você talvez já tenha ouvido falar dela ou até a tenha usado, mas vou explicar alguns exemplos e os princípios matemáticos por trás dela.

Botão de alternância

  • Ao criar componentes de UI, suponha, por exemplo, que você esteja fazendo um botão de alternância.
  • A posição do interruptor do botão é calculada com base no estado: quando está ligado, fica em max_x; quando está desligado, em min_x.
  • Isso funciona bem, mas sem animação parece um pouco sem vida.
  • A animação não só adiciona um toque visual, como também ajuda o usuário a entender o que está acontecendo.
  • Em vez de mover o indicador do toggle imediatamente para a nova posição, ele passa a se mover de forma suave.
  • Agora é preciso atualizar a animação, e isso tem a desvantagem de parecer que ela se move em velocidade constante.
  • Dá para adicionar uma função de easing aqui, por exemplo 3t^2-2t^3 ou sqrt(t).
  • As diferenças entre essas funções de easing ficam mais visíveis quando a animação é reproduzida lentamente.
  • Agora, em vez de atualizar a posição do interruptor, é preciso acompanhar o estado da animação.
  • Ao usar sqrt, é necessário usar explicitamente funções de easing diferentes dependendo da direção da animação.
  • Qual delas parece melhor é uma questão de gosto, mas sqrt parece a melhor. Ela faz o interruptor começar muito rápido, mas desacelerar bem à medida que se aproxima do destino.
  • A desvantagem dessa versão é que, mesmo no caso mais simples, ela exige bastante gerenciamento e, se o usuário clicar no meio da animação, ocorre uma descontinuidade com um salto repentino.

Movimento de câmera

  • Suponha uma situação em que o mapa e a câmera rolam ou se deslocam ao redor.
  • Também faz sentido adicionar animação aqui.
  • É apresentado um código que interpola com velocidade constante.
  • A tremulação que acontece depois que a animação termina ocorre porque target.x - position.x alterna entre valores positivos e negativos.
  • Em vez de sign(delta), é necessária uma função que faça clamp do delta.
  • Esse método é consideravelmente complexo para algo simples.
  • Fica estranho quando a velocidade da animação é maior do que a conclusão da animação.
  • Também seria possível ignorar a entrada do usuário enquanto a animação está ativa, mas isso proporciona uma experiência extremamente irritante.
  • A solução perfeita, claro, é a suavização exponencial.
  • O código quase não muda em comparação com o exemplo do botão de alternância.

Como funciona por dentro

  • É explicado o que é 1 - exp(- speed * dt) e como isso funciona.
  • Começa-se pela versão mais simples, tornando a velocidade proporcional à distância entre a position atual e a nova posição para onde deve se mover, target, fazendo com que o movimento seja mais rápido.
  • Esse método não precisa manter nenhum estado além da posição atual e da posição-alvo, e se ajusta automaticamente mesmo que target mude de repente.
  • Mas há um pequeno problema: se speed * dt for maior que 1, a posição ultrapassa o alvo.
  • Para resolver isso, é possível limitar o valor em 1.
  • O motivo de speed * dt ficar grande demais é que o valor de speed é grande demais ou dt é grande demais.
  • No caso de animações, seria ótimo se tudo funcionasse perfeitamente ao aplicar dt.

Equação diferencial (ah, não)

  • É apresentada uma abordagem em duas etapas para resolver o problema.
  • O fato de position += (target - position) * speed * dt funcionar para dt pequeno, mas falhar para dt grande, é um problema típico de solução numérica de equações diferenciais.
  • É examinado o que essa equação resolve.
  • É explicado que position += (target - position) * (1 - exp(- speed * dt)) é a fórmula correta para qualquer dt.

Escolhendo a velocidade

  • Em geral, pensamos em animação em termos de duração.
  • Ao usar a fórmula exponencial, tecnicamente a animação nunca termina e se completa ao longo de um tempo infinito.
  • O que o parâmetro speed significa é que 1 / speed é o tempo em que position fica e = 2.71828... vezes mais próxima de target.

Suavização exponencial

  • Se você procurar por "suavização exponencial", vai encontrar um artigo da wiki que parece totalmente sem relação, mas que na verdade traz uma fórmula muito parecida com a discutida neste post.
  • Se supusermos que dt é sempre o mesmo e que target muda a cada iteração, podemos indexar os valores pelo número da iteração e calcular algo como position[i] = (target[i] - position[i - 1]) * factor.

Título do último parágrafo

  • Tenho a ideia deste post há vários meses e fico feliz por finalmente tê-lo concluído.
  • Obrigado por acompanhar e ler o log de desenvolvimento.

Opinião do GN⁺

  • Este artigo explica a técnica de suavização exponencial usada para tornar animações mais suaves e naturais. Essa técnica contribui para melhorar a experiência do usuário e aumentar a intuitividade da interface.
  • A suavização exponencial também pode ser útil para simular movimento físico, por exemplo, tornando mais naturais o movimento de personagens ou o deslocamento de câmera no desenvolvimento de jogos.
  • Essa técnica é especialmente eficaz para representar visualmente mudanças de estado em elementos de interface do usuário. Por exemplo, ela pode deixar mais suave o movimento de sliders ou interruptores.
  • De um ponto de vista crítico, a suavização exponencial pode dificultar o controle preciso da velocidade e da duração da animação. Isso pode ser uma limitação quando o designer quer ajustar com precisão um efeito de animação específico.
  • Outras bibliotecas ou frameworks de animação com funcionalidades semelhantes incluem GreenSock Animation Platform(GSAP) e anime.js, que oferecem controle mais refinado da animação junto com várias funções de easing.
  • Ao adotar a técnica de suavização exponencial, é preciso encontrar um equilíbrio entre a naturalidade da animação e a precisão do controle. A vantagem dessa escolha é a melhoria da experiência do usuário; a desvantagem é que o ajuste preciso do timing da animação pode ser difícil.

1 comentários

 
GN⁺ 2024-03-09
Opiniões do Hacker News
  • Resumo do primeiro comentário:

    • Enfatiza que esse método não é uma simples curva de easing nem uma função smoothstep(), mas sim uma abordagem sem estado (stateless) que lida com entradas variadas de forma consistente.
    • Se você já usou transições CSS, consegue entender o problema que essa técnica resolve.
    • A suavização exponencial (exponential smoothing) tem o problema de se aproximar do destino sem nunca realmente alcançá-lo.
    • Ao usar um método parecido com rolagem inercial (inertial scrolling), menciona que foi útil adicionar um termo de atrito (falso).
  • Resumo do segundo comentário:

    • Como desenvolvedor de jogos, prefere tweens com easing e duração predefinida para a maioria dos casos de UI.
    • No entanto, essa técnica é muito útil para suavizar movimentos contínuos e imprevisíveis, em que não há um ponto claro de início e fim.
    • O lerp exponencial (exponential lerp) é útil, mas pouco conhecido, e alguns jogos sofrem com problemas de animação por usarem interpolação linear menos precisa.
    • Por isso, agradece pelo artigo, já que muitas vezes é difícil ter acesso a esse tipo de conhecimento específico.
  • Resumo do terceiro comentário:

    • Discorda da afirmação do autor de que raiz quadrada (sqrt) é melhor do que cúbica (cubic) para interruptores do tipo toggle.
    • Argumenta que uma função cúbica é mais adequada quando se considera como um toggle real funciona.
    • Avalia o artigo como uma boa demonstração de como animações podem melhorar a experiência do usuário.
  • Resumo do quarto comentário:

    • Demonstra admiração por truques não lineares simples que adicionam prazer às interações online.
    • Menciona que isso desempenha um papel importante na percepção de cor e que as pessoas nem sempre entendem aceleração intuitivamente.
  • Resumo do quinto comentário:

    • Diz que gostou do artigo e menciona ter implementado a mesma técnica há quase 10 anos com o nome de lazy-easy.
    • Afirma que ainda usa essa técnica quando quer animações suaves sem gerenciamento de estado.
  • Resumo do sexto comentário:

    • Observa que artigos sobre easing parecem ser redescobertos por cada nova geração.
    • Lembra dos sites experimentais de Yugo Nakamura no fim dos anos 1990 como um dos primeiros exemplos de uso livre de easing para criar uma sensação orgânica.
  • Resumo do sétimo comentário:

    • Sugere a ideia de um toggle que se move lentamente enquanto o toque/clique é mantido e, ao soltar, dá um snap rápido no restante do percurso.
    • Não tem certeza do que isso significaria em termos de UX, mas imagina que poderia indicar o momento em que uma configuração é aplicada ou salva.
  • Resumo do oitavo comentário:

    • Avalia que essa técnica é útil não só para interruptores, mas para vários usos.
    • Menciona uma demo relacionada ao Flickity e aponta que a técnica não incluía várias otimizações necessárias para produção.
    • Critica as pessoas nos comentários por não terem lido o artigo direito ou por estarem deixando passar pontos importantes.
  • Resumo do nono comentário:

    • Faz uma avaliação positiva do artigo, mas relata que, embora a demo funcione bem no Chrome, a página trava no Firefox.
  • Resumo do décimo comentário:

    • Avalia que pequenas animações contam muita coisa e representam a forma mais elevada de design emocional.