Em relação a uma métrica de suavidade da animação

Saiba como medir animações, considerar frames de animação e a suavidade geral da página.

Behdad Bakhshinategh
Behdad Bakhshinategh
Jonathan Ross
Jonathan Ross
Michal Mocny
Michal Mocny

Você provavelmente já encontrou páginas que "travam" ou "congelam" durante a rolagem ou animações. Sabemos que essas experiências não são tranquilas. Para resolver esses tipos de problemas, a equipe do Chrome está trabalhando para adicionar mais suporte às nossas ferramentas de laboratório para detecção de animação, além de fazer melhorias constantes no diagnóstico do pipeline de renderização no Chromium.

Queremos compartilhar alguns progressos recentes, oferecer orientações concretas sobre ferramentas e discutir ideias para futuras métricas de suavidade de animação. Como sempre, gostaríamos de receber seu feedback.

Esta postagem abordará três tópicos principais:

  • Visão rápida das animações e dos frames de animação.
  • Nossos pensamentos atuais sobre como medir a suavidade geral da animação.
  • Algumas sugestões práticas para usar nas ferramentas do laboratório hoje mesmo.

O que são animações?

As animações dão vida ao conteúdo. Ao fazer com que o conteúdo se mova, especialmente em resposta a interações do usuário, as animações podem tornar uma experiência mais natural, compreensível e divertida.

No entanto, animações mal implementadas ou apenas adicionando muitas animações podem degradar a experiência e torná-la simplesmente não divertida. Provavelmente todos nós já interagimos com uma interface que acabou de adicionar muitos efeitos de transição "úteis", que na verdade se tornam hostis quando o desempenho é ruim. Portanto, alguns usuários podem priorizar o movimento reduzido, uma preferência que você precisa respeitar.

Como funcionam as animações?

Recapitulando, o pipeline de renderização consiste em alguns estágios sequenciais:

  1. Estilo:calcule os estilos que se aplicam aos elementos.
  2. Layout:gere a geometria e a posição de cada elemento.
  3. Paint:preencha os pixels de cada elemento em camadas.
  4. Composto:desenhe as camadas na tela.

Embora existam muitas maneiras de definir animações, todas funcionam fundamentalmente com uma destas opções:

  • Ajustar as propriedades de layout.
  • ajustar as propriedades de pintura.
  • ajustar propriedades compostas;

Como esses estágios são sequenciais, é importante definir animações em termos de propriedades que estão mais abaixo no pipeline. Quanto mais cedo a atualização acontecer no processo, maiores serão os custos e menor será a probabilidade de ser tranquilo. Consulte Desempenho de renderização para saber mais.

Embora possa ser conveniente animar propriedades de layout, há custos, mesmo que eles não sejam imediatamente aparentes. As animações precisam ser definidas em termos de mudanças de propriedades compostas sempre que possível.

Definir animações CSS declarativas ou usar animações da Web e garantir a animação de propriedades compostas é uma ótima primeira etapa para garantir animações suaves e eficientes. No entanto, isso por si só não garante a suavidade, porque até mesmo animações eficientes da Web têm limites de desempenho. É por isso que é sempre importante medir!

O que são frames de animação?

As atualizações na representação visual de uma página levam algum tempo para aparecer. Uma mudança visual leva a um novo frame de animação, que é renderizado na tela do usuário.

Mostra atualizações em algum intervalo, então as atualizações visuais são agrupadas. Muitas telas são atualizadas em um intervalo de tempo fixo, como 60 vezes por segundo (ou seja, 60 Hz). Algumas telas mais modernas podem oferecer taxas de atualização mais altas (90 a 120 Hz estão se tornando comuns). Muitas vezes, essas telas podem se adaptar ativamente entre as taxas de atualização, conforme necessário, ou até mesmo oferecer frame rates totalmente variáveis.

O objetivo de qualquer aplicativo, como um jogo ou navegador, é processar todas essas atualizações visuais em lote e produzir um frame de animação visualmente completo dentro do prazo, todas as vezes. Essa meta é totalmente diferente de outras tarefas importantes do navegador, como carregar conteúdo da rede rapidamente ou executar tarefas JavaScript de maneira eficiente.

Em algum momento, pode ser muito difícil concluir todas as atualizações visuais dentro do prazo alocado pela tela. Quando isso acontece, o navegador descarta um frame. A tela não fica preta, ela se repete. Você vai notar a mesma atualização visual por um pouco mais: o mesmo frame de animação que foi apresentado na oportunidade de frame anterior.

Na verdade, isso acontece com frequência! Ela não é necessariamente perceptível, especialmente para conteúdo estático ou semelhante a documentos, o que é comum em particular na plataforma da Web. Os frames descartados só ficam aparentes quando há atualizações visuais importantes, como animações, em que precisamos de um fluxo constante de atualizações de animação para mostrar um movimento suave.

O que afeta os frames de animação?

Os desenvolvedores da Web podem afetar significativamente a capacidade de um navegador de renderizar e apresentar atualizações visuais de maneira rápida e eficiente.

Alguns exemplos:

  • Usar conteúdo muito grande ou que consome muitos recursos para decodificar rapidamente no dispositivo de destino.
  • Uso de muitas camadas exigindo muita memória da GPU.
  • Definir estilos CSS ou animações da Web excessivamente complexos
  • Usar antipadrões de design que desativam as otimizações de renderização rápida.
  • Excesso de trabalho JS na linha de execução principal, levando a tarefas longas que bloqueiam atualizações visuais.

Mas como você pode saber quando um frame de animação perdeu o prazo e causou uma queda de frame?

Um método possível é usar a pesquisa requestAnimationFrame(), mas há várias desvantagens. requestAnimationFrame(), ou "rAF", informa ao navegador que você quer executar uma animação e solicita uma oportunidade de fazer isso antes do próximo estágio de pintura do pipeline de renderização. Se a função de callback não for chamada no momento esperado, isso significa que uma pintura não foi executada e um ou mais frames foram ignorados. Ao pesquisar e contar com que frequência o rAF é chamado, é possível calcular um tipo de métrica de "quadros por segundo" (QPS).

let frameTimes = [];
function pollFramesPerSecond(now) {
  frameTimes = [...frameTimes.filter(t => t > now - 1000), now];
  requestAnimationFrame(pollFramesPerSecond);
  console.log('Frames per second:', frameTimes.length);
}
requestAnimationFrame(pollFramesPerSecond);

Usar a pesquisa de requestAnimationFrame() não é uma boa ideia por vários motivos:

  • Cada script precisa configurar seu próprio loop de pesquisa.
  • Pode bloquear o caminho crítico.
  • Mesmo que a pesquisa de rAF seja rápida, ela pode impedir que o requestIdleCallback() programe longos blocos inativos quando usados continuamente, ou seja, blocos que excedam um único frame.
  • Da mesma forma, a falta de longos blocos inativos impede que o navegador programe outras tarefas de longa duração, como coleta de lixo mais longa e outros trabalhos em segundo plano ou especulativos.
  • Se a sondagem estiver ativada e desativada, você vai perder casos em que o orçamento do frame foi excedido.
  • A pesquisa informará falsos positivos nos casos em que o navegador estiver usando uma frequência de atualização variável (por exemplo, devido à energia ou ao status de visibilidade).
  • E o mais importante, ela não captura todos os tipos de atualizações de animação.

Muito trabalho na linha de execução principal pode afetar a capacidade de ver frames de animação. Confira o Exemplo de instabilidade (link em inglês) para saber como uma animação orientada por rAF, quando há muito trabalho na linha de execução principal (como layout), resulta na queda de frames, em menos callbacks rAF e na redução de QPS.

Quando a linha de execução principal fica sobrecarregada, as atualizações visuais começam a travar. Isso é instabilidade!

Muitas ferramentas de medição se concentraram extensivamente na capacidade de a linha de execução principal gerar resultados em tempo hábil e na execução dos frames de animação sem problemas. Mas esta não é toda a história. Confira o exemplo abaixo:

O vídeo acima mostra uma página que injeta tarefas longas periodicamente na linha de execução principal. Essas tarefas longas prejudicam completamente a capacidade da página de fornecer determinados tipos de atualizações visuais, e é possível notar no canto superior esquerdo uma queda correspondente de FPS informado por requestAnimationFrame() para 0.

Ainda assim, apesar dessas longas tarefas, a página continua rolando suavemente. Isso acontece porque, em navegadores mais recentes, a rolagem geralmente é linha de execução, gerada inteiramente pelo compositor.

Esse é um exemplo que simultaneamente contém muitos frames descartados na linha de execução principal, mas que ainda tem muitos frames de rolagem entregues com sucesso na linha de execução do compositor. Quando a tarefa longa é concluída, a atualização de pintura da linha de execução principal não tem nenhuma mudança visual para oferecer de qualquer maneira. A pesquisa de rAF sugeriu uma queda de frame para 0, mas visualmente, um usuário não conseguiria perceber a diferença.

Para frames de animação, a história não é tão simples.

Frames de animação: atualizações importantes

O exemplo acima mostra que há mais na história do que apenas requestAnimationFrame().

Então, quando as atualizações e os frames de animação são importantes? Veja a seguir alguns critérios que estamos considerando e gostaríamos de receber feedback:

  • Atualizações de linha de execução principal e do compositor
  • Atualizações de pintura ausentes
  • Como detectar animações
  • Qualidade versus quantidade

Atualizações de linha de execução principal e do compositor

As atualizações dos frames de animação não são booleanas. Não é possível que os frames só possam ser totalmente descartados ou totalmente apresentados. Há muitos motivos para um frame de animação ser parcialmente apresentado. Em outras palavras, ele pode ter algum conteúdo desatualizado e, ao mesmo tempo, algumas atualizações visuais apresentadas.

O exemplo mais comum disso é quando o navegador não consegue produzir uma nova atualização da linha de execução principal dentro do prazo do frame, mas tem uma nova atualização de linha de execução do compositor, como o exemplo de rolagem da linha de execução anterior.

Um motivo importante para o uso de animações declarativas para animar propriedades compostas é que isso permite que uma animação seja totalmente conduzida pela linha de execução do compositor, mesmo quando a linha de execução principal está ocupada. Esses tipos de animações podem continuar produzindo atualizações visuais de forma eficiente e em paralelo.

Por outro lado, pode haver casos em que uma atualização da linha de execução principal finalmente fica disponível para apresentação, mas somente depois de perder vários prazos de frames. Aqui o navegador terá alguma atualização nova, mas pode não ser a mais recente.

De um modo geral, consideramos os frames que contêm algumas novas atualizações visuais, sem todas as novas atualizações visuais, como um frame parcial. Frames parciais são bastante comuns. O ideal é que atualizações parciais incluam pelo menos as atualizações visuais mais importantes, como animações, mas isso só poderá acontecer se as animações forem orientadas pela linha de execução do compositor.

Atualizações de pintura ausentes

Outro tipo de atualização parcial é quando mídia, como imagens, não termina de decodificar e rasterizar a tempo para a apresentação do frame.

Ou, mesmo que uma página seja perfeitamente estática, os navegadores ainda podem ficar atrasados na renderização das atualizações visuais durante a rolagem rápida. Isso ocorre porque as execuções de pixel do conteúdo além da janela de visualização visível podem ser descartadas para economizar memória da GPU. A renderização de pixels leva tempo, e pode levar mais do que um único frame para renderizar tudo após uma rolagem grande, como deslizar o dedo. Isso é comumente conhecido como tabulação de xadrez.

A cada oportunidade de renderização de frame, é possível rastrear quantas atualizações visuais mais recentes realmente chegaram à tela. Medir essa capacidade durante muitos frames (ou tempo) é muito conhecido como capacidade de processamento de frames.

Se a GPU estiver muito sobrecarregada, o navegador (ou a plataforma) poderá até começar a limitar a taxa de tentativas de atualizações visuais e, assim, diminuir os frame rates efetivos. Embora tecnicamente isso possa reduzir o número de atualizações de frames descartadas, visualmente ele ainda aparecerá como uma capacidade de frame menor.

No entanto, nem todos os tipos de baixa capacidade de processamento de frames são ruins. Se a página estiver quase ociosa e não houver animações ativas, um frame rate baixo será tão visualmente atraente quanto um frame rate alto (e pode economizar bateria).

Então, quando a capacidade de processamento de frames é importante?

Como detectar animações

A alta capacidade de processamento de frames é importante especialmente durante períodos com animações importantes. Diferentes tipos de animação dependem de atualizações visuais de uma linha de execução específica (principal, compositor ou um worker). Portanto, a atualização visual depende dessa linha de execução dentro do prazo. Dizemos que uma determinada linha de execução afeta a suavidade sempre que há uma animação ativa que depende dessa atualização.

Alguns tipos de animação são mais fáceis de definir e detectar do que outros. Animações declarativas, ou animações orientadas por entrada do usuário, são mais claras de definir do que animações orientadas por JavaScript implementadas como atualizações periódicas em propriedades de estilo animáveis.

Mesmo com requestAnimationFrame(), nem sempre é possível presumir que cada chamada de rAF está necessariamente produzindo uma atualização ou animação visual. Por exemplo, o uso de pesquisas de rAF apenas para rastrear o frame rate (como mostrado acima) não afeta as medições de suavidade, já que não há atualização visual.

Qualidade versus quantidade

Por fim, a detecção de animações e atualizações de frames de animação ainda é apenas uma parte da história, porque captura apenas a quantidade de atualizações da animação, não a qualidade.

Por exemplo, você pode notar um frame rate constante de 60 QPS enquanto assiste um vídeo. Tecnicamente, isso é perfeitamente tranquilo, mas o vídeo em si pode ter uma taxa de bits baixa ou problemas com o armazenamento em buffer da rede. Isso não é capturado diretamente pelas métricas de suavidade da animação, mas ainda pode ser desagradável para o usuário.

Ou um jogo que usa <canvas> (talvez até mesmo usando técnicas como tela fora da tela para garantir um frame rate estável) pode ser tecnicamente perfeitamente uniforme em termos de frames de animação, sem carregar recursos de jogos de alta qualidade na cena ou exibir artefatos de renderização.

E, claro, um site pode ter algumas animações muito ruins 🙂

GIF de escola em construção

Quero dizer, acho que eles eram incríveis para a época!

Estados de um único frame de animação

Como os frames podem ser parcialmente apresentados ou perdidos podem acontecer de maneiras que não afetam a suavidade, começamos a pensar em cada frame como tendo uma pontuação de integridade ou suavidade.

Confira o espectro de maneiras de interpretar o estado de um único frame de animação, ordenado do melhor para o pior caso:

Nenhuma atualização necessária Tempo de inatividade, repetição do frame anterior.
Totalmente apresentado A atualização da linha de execução principal foi confirmada dentro do prazo ou nenhuma atualização da linha de execução principal foi desejada.
Parcialmente apresentado Somente composto. A atualização atrasada da linha de execução principal não teve mudança visual.
Parcialmente apresentado Somente composto. A linha de execução principal teve uma atualização visual, mas essa atualização não incluiu uma animação que afeta a suavidade.
Parcialmente apresentado Somente o compostor. A linha de execução principal tinha uma atualização visual que afeta a suavidade, mas um frame antigo chegou e foi usado.
Parcialmente apresentado Somente o compositor; sem a atualização principal desejada, e a atualização do compositor tem uma animação que afeta a suavidade.
Parcialmente apresentado Somente o compostor, mas a atualização dele não tem uma animação que afete a suavidade.
Frame descartado Nenhuma atualização. Não foi necessário atualizar o compositor, e o main foi atrasado.
Frame descartado Uma atualização do compositor era desejada, mas estava atrasada.
Frame desatualizado Você queria uma atualização, ela foi produzida pelo renderizador, mas a GPU ainda não a apresentou antes do prazo de vsync.

É possível transformar esses estados em uma pontuação. E talvez uma maneira de interpretar essa pontuação seja considerar que ela é uma probabilidade de ser observável pelo usuário. Um único frame descartado pode não ser muito observável, mas uma sequência de muitos frames descartados afetando a suavidade em uma linha com certeza é.

Como reunir tudo: métrica "Porcentagem de frames perdidos"

Embora possa ser necessário analisar detalhadamente o estado de cada frame de animação, também é útil atribuir uma pontuação rápida para uma experiência.

Como os frames podem ser parcialmente apresentados, e mesmo as atualizações totalmente ignoradas de frames podem não afetar a suavidade, queremos nos concentrar menos na contagem de frames e mais na extensão em que o navegador não consegue fornecer atualizações visualmente completas quando for importante.

O modelo mental precisa passar de:

  1. Quadros por segundo, para
  2. Detectar atualizações de animação ausentes e importantes para
  3. Porcentagem de queda em um determinado período.

O que importa é a proporção de tempo de espera por atualizações importantes. Acreditamos que isso corresponde à experiência natural dos usuários com a suavidade do conteúdo da Web na prática. Até agora, usamos o seguinte como conjunto inicial de métricas:

  • Média de porcentagem descartada:para todos os frames de animação não inativos em toda a linha do tempo.
  • Pior caso de frames perdidos: conforme medido em janelas de tempo deslizantes de um segundo.
  • 95o percentil de frames perdidos:conforme medido em janelas de tempo deslizantes de 1 segundo.

Essas pontuações estão disponíveis em algumas ferramentas para desenvolvedores do Chrome hoje mesmo. Embora essas métricas se concentrem apenas na capacidade geral do frame, também estamos avaliando outros fatores, como a latência do frame.

Teste você mesmo nas ferramentas para desenvolvedores.

HUD de desempenho

O Chromium tem um HUD de desempenho legal oculto por uma flag (chrome://flags/#show-performance-metrics-hud). Nele, você pode encontrar pontuações em tempo real para, por exemplo, Core Web Vitals e também algumas definições experimentais para a suavidade da animação com base na Percentual de frames descartados ao longo do tempo.

HUD de desempenho

Estatísticas de renderização de frames

Ative as "Estatísticas de renderização de frames" no DevTools nas configurações de renderização para conferir imagens ao vivo dos novos frames de animação, que são codificados por cores para diferenciar atualizações parciais das atualizações de frames totalmente descartadas. O QPS informado é apenas para frames totalmente apresentados.

Estatísticas de renderização de frames

Visualizador de frames nas gravações do perfil de desempenho do DevTools

O painel DevTools Performance tem um visualizador de frames há muito tempo. No entanto, ele ficou um pouco fora de sincronia com a forma como o pipeline de renderização moderno funciona. Houve muitas melhorias recentes, mesmo na versão mais recente do Chrome Canary. Acreditamos que isso vai facilitar muito a depuração de problemas de animação.

Hoje, você descobrirá que os frames no visualizador de frames estão melhor alinhados com os limites de Vsync e são codificados por cores com base no status. Ainda não há visualização completa para todas as nuances descritas acima, mas estamos planejando adicionar mais em breve.

Visualizador de frames no Chrome DevTools

Rastreamento do Chrome

Por fim, com o Rastreamento do Chrome, a ferramenta ideal para mergulhar em detalhes, você pode gravar um rastro de "Renderização de conteúdo da Web" usando a nova interface do Perfetto (ou about:tracing) e se aprofundar no pipeline de gráficos do Chrome. Pode ser uma tarefa assustadora, mas há algumas coisas recentemente adicionadas ao Chromium para facilitar isso. Você pode ter uma visão geral do que está disponível no documento O ciclo de um frame.

Com os eventos de rastreamento, você pode determinar de forma definitiva:

  • Quais animações estão sendo executadas (usando eventos chamados TrackerValidation).
  • Acessar a linha do tempo exata dos frames de animação usando eventos chamados PipelineReporter.
  • Para atualizações de animação instáveis, descubra exatamente o que está impedindo que a animação seja executada mais rapidamente (usando os detalhamentos dos eventos de PipelineReporter).
  • Para animações orientadas por entrada, confira quanto tempo leva para receber uma atualização visual (usando eventos chamados EventLatency).

Registrador de pipeline do Rastreamento do Chrome

A seguir

O objetivo da iniciativa Métricas da Web é fornecer métricas e orientações para criar ótimas experiências do usuário na Web. As métricas baseadas em laboratório, como o Tempo de bloqueio total (TBT, na sigla em inglês) são vitais para detectar e diagnosticar possíveis problemas de interatividade. Estamos planejando projetar uma métrica semelhante com base em laboratório para a suavidade da animação.

Vamos manter você por dentro enquanto continuamos trabalhando em ideias para criar uma métrica abrangente com base em dados de frames de animação individuais.

No futuro, também queremos projetar APIs que permitam medir o desempenho da suavidade da animação para usuários reais no campo e no laboratório. Fique de olho nas atualizações por lá também.

Feedback

Estamos empolgados com todas as melhorias e ferramentas para desenvolvedores recentes que foram lançadas no Chrome para medir a suavidade da animação. Teste essas ferramentas, compare suas animações e nos conte aonde ela chegará.

Envie seus comentários para o grupo do Google web-vitals-feedback com "[Métricas de suavidade]" na linha de assunto. Estamos ansiosos para saber sua opinião.