API Long Animation Frames

A API Long Animation Frames (LoAF, pronunciado Lo-Af) é uma atualização da API Long Tasks para oferecer uma melhor compreensão das atualizações lentas da interface do usuário (IU). Isso pode ser útil para identificar frames de animação lentos que provavelmente afetam a métrica Core Web Vitals (INP, na sigla em inglês) de Interaction to Next Paint, que mede a capacidade de resposta, ou para identificar outras instabilidades da interface que afetam a suavidade.

Status da API

Compatibilidade com navegadores

  • 123
  • x
  • x
  • x

Após um teste de origem do Chrome 116 ao Chrome 122, a API LoAF foi enviada do Chrome 123.

API Long Tasks

Compatibilidade com navegadores

  • 58
  • 79
  • x
  • x

Origem

A API Long Animation Frames é uma alternativa à API Long Tasks, que está disponível no Chrome há algum tempo (desde o Chrome 58). Como o nome sugere, a API Long Task permite monitorar tarefas longas, que são tarefas que ocupam a linha de execução principal por 50 milissegundos ou mais. Tarefas longas podem ser monitoradas usando a interface PerformanceLongTaskTiming com um PeformanceObserver:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'longtask', buffered: true });

Tarefas longas provavelmente causam problemas de capacidade de resposta. Se um usuário tentar interagir com uma página (por exemplo, clicar em um botão ou abrir um menu), mas a linha de execução principal já estiver lidando com uma tarefa longa, a interação do usuário será atrasada e, em seguida, a tarefa será concluída.

Para melhorar a capacidade de resposta, geralmente é aconselhável dividir tarefas longas. Se cada tarefa longa for dividida em uma série de várias tarefas menores, isso poderá permitir que tarefas mais importantes sejam executadas entre elas para evitar atrasos significativos na resposta às interações.

Portanto, ao tentar melhorar a capacidade de resposta, o primeiro esforço costuma ser executar um rastreamento de desempenho e analisar tarefas longas. Isso pode ser feito com uma ferramenta de auditoria baseada em laboratório, como o Lighthouse (que tem uma auditoria Evitar longas tarefas de linha de execução principal) ou verificando tarefas longas no Chrome DevTools.

Os testes baseados em laboratório costumam ser um ponto de partida ruim para identificar problemas de capacidade de resposta, já que essas ferramentas podem não incluir interações. Quando isso acontece, formam um pequeno subconjunto das prováveis interações. O ideal é medir as causas de interações lentas no campo.

Falhas da API Long Tasks

Medir tarefas longas em campo usando um Observador de desempenho é apenas um pouco útil. Na realidade, ele não oferece tantas informações além do fato de que uma tarefa longa aconteceu e quanto tempo ela levou.

As ferramentas de monitoramento de usuários reais (RUM, na sigla em inglês) geralmente usam isso para indicar o número ou a duração de tarefas longas ou identificar em quais páginas elas acontecem. No entanto, sem os detalhes subjacentes sobre o que causou a tarefa longa, o uso é limitado. A API Long Tasks tem apenas um modelo de atribuição básico, que, na melhor das hipóteses, informa apenas o contêiner em que a tarefa longa ocorreu (o documento de nível superior ou um <iframe>), mas não o script ou a função que a chamou, como mostrado em uma entrada típica:

{
  "name": "unknown",
  "entryType": "longtask",
  "startTime": 31.799999997019768,
  "duration": 136,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}

A API Long Tasks também é uma visualização incompleta, já que também pode excluir algumas tarefas importantes. Algumas atualizações, como a renderização, acontecem em tarefas separadas que, idealmente, devem ser incluídas junto com a execução anterior que fez com que essa atualização meça com precisão o "trabalho total" dessa interação. Para mais detalhes sobre as limitações do uso de tarefas, consulte a seção "Onde tarefas longas ficam aquém do esperado" da explicação.

A questão final é que a medição de tarefas longas só gera relatórios sobre tarefas individuais que levam mais do que o limite de 50 milissegundos. Um frame de animação pode ter várias tarefas abaixo desse limite de 50 milissegundos, mas coletivamente ainda bloqueiam a capacidade de renderização do navegador.

API Long Animation Frames

Compatibilidade com navegadores

  • 123
  • x
  • x
  • x

A API Long Animation Frames (LoAF) é uma nova API que busca resolver algumas das deficiências da API Long Tasks para permitir que os desenvolvedores tenham insights mais úteis para ajudar a resolver problemas de capacidade de resposta e melhorar o INP.

Uma boa capacidade de resposta significa que a página responde rapidamente a interações feitas com ela. Isso envolve a capacidade de pintar as atualizações necessárias para o usuário em tempo hábil e evitar o bloqueio delas. Para INP, recomendamos que você responda em 200 milissegundos ou menos, mas para outras atualizações (por exemplo, animações), mesmo que elas sejam muito longas.

A API Long Animation Frames é uma abordagem alternativa para medir o trabalho de bloqueio. Em vez de medir as tarefas individuais, a API Long Animation Frames, como o nome sugere, mede frames de animação longos. Um frame longo de animação ocorre quando uma atualização de renderização é atrasada além de 50 milissegundos (o mesmo que o limite da API Long Tasks).

Frames de animação longos podem ser observados de maneira semelhante a tarefas longas com um PerformanceObserver, mas analisando o tipo long-animation-frame:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Frames longos anteriores também podem ser consultados no Cronograma de desempenho da seguinte forma:

const loafs = performance.getEntriesByType('long-animation-frame');

No entanto, há um maxBufferSize para entradas de desempenho depois do qual as entradas mais recentes são descartadas. Portanto, a abordagem PerformanceObserver é a recomendada. O tamanho do buffer long-animation-frame é definido como 200, o mesmo que para long-tasks.

Vantagens de analisar frames em vez de tarefas

A principal vantagem de analisar isso de uma perspectiva de frame em vez de tarefas é que uma animação longa pode ser composta de qualquer número de tarefas que resultem cumulativamente em um longo frame de animação. Isso aborda o ponto final mencionado anteriormente, em que a soma de muitas tarefas menores de bloqueio de renderização antes de um frame de animação pode não ser exibida pela API Long Tasks.

Outra vantagem dessa visualização alternativa para tarefas longas é a capacidade de fornecer detalhamentos de tempo de todo o frame. Em vez de apenas incluir um startTime e um duration, como a API Long Tasks, o LoAF inclui um detalhamento muito mais detalhado das várias partes da duração do frame, incluindo:

  • startTime: o horário de início do frame longo da animação em relação ao horário de início da navegação.
  • duration: a duração do frame longo da animação, sem incluir o tempo da apresentação.
  • renderStart: o horário de início do ciclo de renderização, que inclui callbacks requestAnimationFrame, cálculo de estilo e layout, além de callbacks do observador de redimensionamento e do observador de interseção.
  • styleAndLayoutStart: o início do período gasto nos cálculos de estilo e layout.
  • firstUIEventTimestamp: o horário do primeiro evento de interface (mouse/teclado e assim por diante) a ser processado durante o frame.
  • blockingDuration: duração em milissegundos em que o frame da animação estava sendo bloqueado.

Esses carimbos de data/hora permitem que o frame longo da animação seja dividido em marcações de tempo:

Cronograma Cálculo
Horário de início startTime
Horário de término startTime + duration
Duração do trabalho renderStart ? renderStart - startTime : duration
Duração da renderização renderStart ? (startTime + duration) - renderStart: 0
Renderização: duração pré-layout styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Renderização: duração do estilo e do layout styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Para saber mais sobre esses tempos individuais, consulte a explicação, que fornece detalhes sobre qual atividade está contribuindo para um frame de animação longo.

Melhor atribuição

O tipo de entrada long-animation-frame inclui dados de atribuição melhores de cada script que contribuíram para um frame de animação longo.

Semelhante à API Long Tasks, isso será fornecido em uma matriz de entradas de atribuição, cada uma das quais detalha:

  • Tanto name quanto EntryType retornarão script.
  • Um invoker significativo, indicando como o script foi chamado (por exemplo, 'IMG#id.onload', 'Window.requestAnimationFrame' ou 'Response.json.then').
  • O invokerType do ponto de entrada do script:
    • user-callback: um callback conhecido registrado em uma API de plataforma da Web (por exemplo, setTimeout, requestAnimationFrame).
    • event-listener: um listener para um evento de plataforma (por exemplo, click, load, keyup).
    • resolve-promise: gerenciador de uma promessa da plataforma (por exemplo, fetch(). No caso de promessas, todos os gerenciadores das mesmas promessas são misturados como um "script"..
    • reject-promise: de acordo com resolve-promise, mas para a rejeição.
    • classic-script: avaliação do script (por exemplo, <script> ou import())
    • module-script: igual a classic-script, mas para scripts de módulo.
  • Dados de tempo separados para esse script:
    • startTime: hora em que a função de entrada foi invocada.
    • duration: a duração entre startTime e quando a fila de microtarefas seguinte termina o processamento.
    • executionStart: tempo após a compilação.
    • forcedStyleAndLayoutDuration: o tempo total gasto no processamento de layout/estilo forçados dentro dessa função. Consulte a sobrecarga.
    • pauseDuration: tempo total gasto em operações síncronas de "pausa" (alerta, XHR síncrono).
  • Detalhes da origem do script:
    • sourceURL: o nome do recurso do script, quando disponível (ou vazio se não for encontrado).
    • sourceFunctionName: o nome da função do script, quando disponível (ou vazio se não for encontrado).
    • sourceCharPosition: a posição do caractere de script, quando disponível (ou -1, se não for encontrado).
  • windowAttribution: o contêiner (o documento de nível superior ou uma <iframe>) em que o frame longo de animação ocorreu.
  • window: uma referência para a janela de mesma origem.

Quando fornecidas, as entradas de origem permitem que os desenvolvedores saibam exatamente como cada script no frame longo de animação foi chamado, até a posição do caractere no script de chamada. Isso informa o local exato em um recurso JavaScript que resultou no frame longo de animação.

Exemplo de uma entrada de desempenho de long-animation-frame

Um exemplo completo de entrada de desempenho de long-animation-frame, que contém um único script, é:

{
  "blockingDuration": 0,
  "duration": 60,
  "entryType": "long-animation-frame",
  "firstUIEventTimestamp": 11801.099999999627,
  "name": "long-animation-frame",
  "renderStart": 11858.800000000745,
  "scripts": [
    {
      "duration": 45,
      "entryType": "script",
      "executionStart": 11803.199999999255,
      "forcedStyleAndLayoutDuration": 0,
      "invoker": "DOMWindow.onclick",
      "invokerType": "event-listener",
      "name": "script",
      "pauseDuration": 0,
      "sourceURL": "https://web.dev/js/index-ffde4443.js",
      "sourceFunctionName": "myClickHandler",
      "sourceCharPosition": 17796,
      "startTime": 11803.199999999255,
      "window": [Window object],
      "windowAttribution": "self"
    }
  ],
  "startTime": 11802.400000000373,
  "styleAndLayoutStart": 11858.800000000745
}

Como pode ser visto, isso fornece uma quantidade sem precedentes de dados para que os sites possam entender a causa das atualizações lentas de renderização.

Como ativar a API Long Animation Frames

A API Long Animation Frames é ativada por padrão desde o Chrome 123.

Como usar a API Long Animation Frames no campo

Ferramentas como o Lighthouse, embora úteis para descobrir e reproduzir problemas, são ferramentas de laboratório que podem perder aspectos importantes da experiência do usuário que apenas dados de campo oferecem. A API Long Animation Frames pode ser usada no campo para coletar dados contextuais importantes para interações de usuários que a API Long Tasks não conseguiria. Isso pode ajudar você a identificar e reproduzir problemas com interatividade que não teria sido possível de outra forma.

Algumas estratégias sugeridas são listadas a seguir, mas a equipe do Google Chrome quer receber feedback sobre essa API e como os desenvolvedores e fornecedores de RUM se veriam usando as informações fornecidas pela API.

Suporte à detecção de recursos da API Long Animation Frames

Você pode usar o código a seguir para testar se a API é compatível:

if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  // Monitor LoAFs
}

A alternativa abaixo pode ser usada nesse caso enquanto frames de animação longos ainda não são compatíveis por padrão e estão neste estado de transição:

if ('PerformanceLongAnimationFrameTiming' in window) {
  // Monitor LoAFs
}

Como informar dados de animação longos de volta a um endpoint de análise

Conforme mostrado, a entrada de desempenho do LoAF inclui informações valiosas. Uma estratégia seria monitorar todos os LoAFs e enviar os que estão acima de um determinado limite para um endpoint de análise para análise posterior:

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.duration > REPORTING_THRESHOLD_MS) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Como as entradas de frames de animação longos podem ser muito grandes, os desenvolvedores precisam decidir quais dados da entrada serão enviados à análise. Por exemplo, os tempos de resumo da entrada e talvez os nomes dos scripts ou algum outro conjunto mínimo de outros dados contextuais que podem ser considerados necessários.

Observar os piores frames longos de animação

Os sites podem coletar dados nos frames de animação mais longos para reduzir o volume de dados que precisam ser beacons. Portanto, não importa quantos frames de animação longos uma página tenha, apenas os dados do pior, cinco ou quantos frames de animação longos forem absolutamente necessários serão referenciados.

MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];

const observer = new PerformanceObserver(list => {
  longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
    (a, b) => b.blockingDuration - a.blockingDuration
  ).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });

No momento adequado (de preferência no evento visibilitychange), envie o beacon de volta para a análise. Para testes locais, use console.table periodicamente:

console.table(longestBlockingLoAFs);

Como vincular à interação INP mais longa

Como extensão da observação dos piores LoAFs, os frames LoAF correspondentes à entrada INP podem ser usados como dados de atribuição para fornecer mais detalhes sobre como melhorar o INP.

No momento, não há uma API direta para vincular uma entrada INP à entrada ou às entradas do LoAF relacionadas, embora seja possível fazer isso no código comparando os horários de início e término de cada um (consulte este script de exemplo).

Como gerar relatórios de frames de animação longos com interações

Uma abordagem alternativa que requer menos código seria sempre enviar as maiores entradas LoAF (ou as maiores X maiores) em que ocorreu uma interação durante o frame (o que pode ser detectado pela presença de um valor firstUIEventTimestamp). Na maioria dos casos, isso inclui a interação INP para uma determinada visita e, em casos raros, quando não aparece, ainda mostra interações longas que são importantes de corrigir, já que podem ser a interação INP para outros usuários.

O código a seguir registra todas as entradas LoAF superiores a 150 milissegundos em que ocorreu uma interação durante o frame. O valor de 150 é escolhido aqui porque é ligeiramente inferior ao limite de INP "bom" de 200 milissegundos. Você pode escolher um valor maior ou menor dependendo das suas necessidades.

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
      if (entry.duration > REPORTING_THRESHOLD_MS &&
        entry.firstUIEventTimestamp > 0
      ) {
        // Example here logs to console, but could also report back to analytics
        console.log(entry);
      }
    }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Como identificar padrões comuns em frames de animação longos

Uma estratégia alternativa seria observar os scripts comuns que aparecem mais em entradas de frames longos de animação. Os dados podem ser relatados em nível de script e/ou posição de personagem para identificar infratores reincidentes.

Isso pode funcionar especialmente bem para plataformas personalizáveis nas quais os temas ou plug-ins que causam problemas de desempenho podem ser mais facilmente identificados em vários sites.

O tempo de execução de scripts comuns de scripts comuns (ou de origens de terceiros) em frames de animação longos pode ser resumido e informado para identificar contribuintes comuns para longos frames de animação em um site ou conjunto de sites. Por exemplo, para analisar URLs:

const observer = new PerformanceObserver(list => {
  const allScripts = list.getEntries().flatMap(entry => entry.scripts);
  const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
  const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
      allScripts.filter(script => script.sourceURL === sourceURL)
  ]));
  const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
    sourceURL,
    count: scripts.length,
    totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
  }));
  processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
  // Example here logs to console, but could also report back to analytics
  console.table(processedScripts);
});

observer.observe({type: 'long-animation-frame', buffered: true});

E um exemplo dessa saída é:

(index) sourceURL count totalDuration
0 'https://example.consent.com/consent.js' 1 840
1 'https://example.com/js/analytics.js' 7 628
2 'https://example.chatapp.com/web-chat.js' 1 5

Como usar a API Long Animation Frames nas ferramentas

A API também pode permitir outras ferramentas para desenvolvedores para depuração local. Embora algumas ferramentas, como o Lighthouse e o Chrome DevTools, tenham conseguido coletar grande parte desses dados usando detalhes de rastreamento de nível mais baixo, ter essa API de nível mais alto pode permitir que outras ferramentas acessem esses dados.

Exibir dados de frames de animação longos no DevTools

É possível exibir frames de animação longos no DevTools usando a API performance.measure(), que é exibida na faixa de velocidade do usuário do DevTools nos rastros de desempenho para mostrar onde concentrar seus esforços para melhorar o desempenho:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Se essa API se mostrar útil a longo prazo, ela provavelmente será incorporada ao próprio DevTools, mas o snippet de código anterior permite que ela apareça nesse processo.

Usar dados de frames de animação longos em outras ferramentas para desenvolvedores

A extensão Métricas da Web mostrou o valor no registro de informações de depuração resumidas para diagnosticar problemas de desempenho. Agora que a API foi lançada, ferramentas como essa podem mostrar dados com mais facilidade para informar aos desenvolvedores onde concentrar os esforços. Também planejamos adicioná-lo à biblioteca JavaScript do Web Vitals na versão 4.

Uso de dados de frames de animação longos em ferramentas de teste automatizadas

Da mesma forma, ferramentas de teste automatizadas, talvez em pipelines de CI/CD, podem revelar detalhes sobre possíveis problemas de desempenho medindo quadros de animação longos durante a execução de vários conjuntos de testes.

Perguntas frequentes

Algumas perguntas frequentes sobre essa API incluem:

Por que não apenas estender ou iterar na API Long Tasks?

Essa é uma abordagem alternativa para relatar uma medição semelhante, mas diferentemente, de possíveis problemas de capacidade de resposta. É importante garantir que os sites que dependem da API Long Tasks continuem funcionando para evitar a interrupção dos casos de uso atuais.

Embora a API Long Tasks possa se beneficiar de alguns recursos do LoAF (como um modelo de atribuição melhor), acreditamos que focar em frames em vez de tarefas oferece muitos benefícios que fazem dessa uma API fundamentalmente diferente da API Long Tasks existente.

Ele vai substituir a API Long Tasks?

Embora acreditemos que a API Long Animation Frames seja uma API melhor e mais completa para medir tarefas longas, no momento não há planos de descontinuar a API Long Tasks.

Feedback solicitado

Acesse a lista de problemas do GitHub para dar feedback ou informar bugs na implementação da API no Chrome no Issue Tracker do Chrome.

Conclusão

A API Long Animation Frames é uma nova API interessante, com muitas possíveis vantagens em relação à API Long Tasks anterior.

Ele está provando ser uma ferramenta fundamental para lidar com problemas de capacidade de resposta, conforme medido pelo INP. O INP é uma métrica desafiadora de se otimizar, e essa API é uma das formas com que a equipe do Chrome está buscando facilitar a identificação e a resolução de problemas para os desenvolvedores.

O escopo da API Long Animation Frames é mais do que INP e pode ajudar a identificar outras causas de atualizações lentas que podem afetar a suavidade geral da experiência do usuário de um site.

Agradecimentos

Imagem em miniatura de Henry Be no Unsplash (links em inglês).