API Long Animation Frames

A API Long Animation Frames (Lo-Af, pronunciada por LoAF) é uma atualização da API Long Tasks para oferecer uma melhor compreensão de atualizações lentas da interface do usuário (IU). Isso pode ser útil para identificar frames de animação lentos que provavelmente vão afetar a métrica Interaction to Next Paint (INP), que mede a capacidade de resposta, ou para identificar outra instabilidade da interface que afeta a suavidade.

Status da API

Compatibilidade com navegadores

  • 123
  • 123
  • x
  • x

Original

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

Contexto: a API Long Tasks

Compatibilidade com navegadores

  • 58
  • 79
  • x
  • x

Original

A API Long Animation Frames é uma alternativa à Long Tasks API, que já 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 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 causarão 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 ficará atrasada aguardando a conclusão dessa tarefa.

Para melhorar a capacidade de resposta, geralmente é aconselhável interromper tarefas longas. Se cada tarefa longa for dividida em uma série de várias tarefas menores, isso pode 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 geralmente é 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 tarefas longas da linha de execução principal, ou analisando tarefas longas no Chrome DevTools.

Os testes baseados em laboratório geralmente são 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, elas são um pequeno subconjunto de interações prováveis. O ideal é medir as causas de interações lentas no campo.

Falhas na API Long Tasks

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

As ferramentas de monitoramento do usuário real (RUM, na sigla em inglês) geralmente usam isso para tendências do número ou da duração de tarefas longas ou para identificar em quais páginas elas acontecem. Mas, sem os detalhes subjacentes do que causou a tarefa longa, isso é apenas de 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 aconteceu (o documento de nível superior ou um <iframe>), mas não o script ou a função que a chamou, como mostrado por 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, preferencialmente, devem ser incluídas com a execução anterior que fez com que essa atualização medisse com precisão o "trabalho total" dessa interação. Para saber mais sobre as limitações de confiar em tarefas, consulte a seção "Onde as tarefas longas são insuficientes" da explicação.

O problema final é que a medição de tarefas longas informa apenas tarefas individuais que levam mais do que o limite de 50 milissegundos. Um frame de animação pode ser composto de várias tarefas menores do que esse limite de 50 milissegundos, mas ainda assim bloquear a capacidade de renderização do navegador.

API Long Animation Frames

Compatibilidade com navegadores

  • 123
  • 123
  • x
  • x

Original

A Long Animation Frames API (LoAF, na sigla em inglês) é uma nova API que busca solucionar algumas das deficiências da Long Tasks API para permitir que os desenvolvedores recebam insights mais acionáveis para ajudar a resolver problemas de capacidade de resposta e melhorar o INP.

Uma boa capacidade de resposta significa que uma página responde rapidamente às interações feitas com ela. Isso envolve conseguir pintar todas as atualizações necessárias para o usuário em tempo hábil e evitar o bloqueio dessas atualizações. Para o INP, recomenda-se responder em até 200 milissegundos, mas para outras atualizações (por exemplo, animações) até que sejam muito longas.

A API Long Animation Frames é uma abordagem alternativa para medir o trabalho de bloqueio. Em vez de medir tarefas individuais, a API Long Animation Frames, como o nome sugere, mede frames de animação longos. Um frame de animação longo é 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 forma semelhante a tarefas longas com uma 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 });

Os frames de animação longos anteriores também podem ser consultados na linha do tempo de performance da seguinte forma:

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

No entanto, há um maxBufferSize para entradas de desempenho. Depois disso, 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 olhar para frames em vez de tarefas

A principal vantagem de analisar isso da perspectiva do frame em vez da perspectiva das tarefas é que uma animação longa pode ser composta por qualquer número de tarefas que resultaram, de forma cumulativa, 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.

Uma 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 uma divisão muito mais detalhada 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 de apresentação).
  • renderStart: o horário de início do ciclo de renderização, que inclui callbacks requestAnimationFrame, cálculo de estilo e layout e callbacks do observador de redimensionamento e do observador de interseção.
  • styleAndLayoutStart: o início do período gasto em cálculos de estilo e layout.
  • firstUIEventTimestamp: o tempo do primeiro evento da interface (mouse/teclado e assim por diante) a ser processado durante o frame.
  • blockingDuration: a duração em milissegundos em que o frame da animação ficou bloqueado.

Essas marcações de tempo permitem que o frame longo da animação seja dividido em marcações de tempo:

Marcação de tempo 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 contribuiu para um longo frame de animação.

Semelhante à API Long Tasks, ele será fornecido em uma matriz de entradas de atribuição, cada uma detalhada:

  • name e 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 de 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 de script (por exemplo, <script> ou import())
    • module-script: igual a classic-script, mas para scripts de módulo.
  • Separe os dados de tempo para esse script:
    • startTime: horário em que a função de entrada foi invocada.
    • duration: a duração entre startTime e quando a fila de microtarefas subsequente termina do processamento.
    • executionStart: o tempo após a compilação.
    • forcedStyleAndLayoutDuration: o tempo total gasto no processamento de layouts e estilos forçados dentro dessa função (consulte sobrecarga).
    • pauseDuration: tempo total gasto em operações síncronas "pausadas" (alerta, XHR síncrono).
  • Detalhes da origem do script:
    • sourceURL: o nome do recurso de 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 um <iframe>) em que o frame longo de animação ocorreu.
  • window: uma referência à janela de mesma origem.

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

Exemplo de uma entrada de performance long-animation-frame

Um exemplo completo de entrada de desempenho de long-animation-frame, contendo 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 oferece uma quantidade sem precedentes de dados para que os sites possam entender a causa das atualizações com renderização lenta.

Usar a API Long Animation Frames no campo

Ferramentas como o Chrome DevTools e o Lighthouse são úteis para descobrir e reproduzir problemas, mas são ferramentas do laboratório que podem deixar passar aspectos importantes da experiência do usuário que apenas os dados de campo podem oferecer.

A API Long Animation Frames foi projetada para ser usada no campo e coletar dados contextuais importantes para interações do usuário que a API Long Tasks não podia. Isso pode ajudar a identificar e reproduzir problemas com interatividade que você não teria descoberto de outra forma.

Suporte para detecção de recursos à API de frames de animação longos

Use o código a seguir para testar se a API é compatível:

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

O caso de uso mais óbvio da API Long Animation Frames é ajudar a diagnosticar e corrigir problemas de Interaction to Next Paint (INP), e esse foi um dos principais motivos para a equipe do Chrome desenvolver essa API. Um bom INP é quando todas as interações são respondidas em 200 milissegundos ou menos desde a interação até o frame ser pintado. Como a API Long Animation Frames mede todos os frames que levam 50 ms ou mais, a maioria dos INPs problemáticos precisa incluir dados de LoAF para ajudar você a diagnosticar essas interações.

O "INP LoAF" é o LoAF que inclui a interação INP, conforme mostrado no diagrama a seguir:

Exemplos de frames de animação longos em uma página, com o INP LoAF destacado.
Uma página pode ter muitos LoAFs, um dos quais está relacionado à interação INP.

Em alguns casos, é possível que um evento INP abranja dois LoAFs, normalmente se a interação ocorrer depois que o frame tiver iniciado a parte de renderização do frame anterior e, assim, o manipulador de eventos processar o evento no frame seguinte:

Exemplos de frames de animação longos em uma página, com o INP LoAF destacado.
Uma página pode ter muitos LoAFs, um dos quais está relacionado à interação INP.

Também é possível que ele abranja mais de dois LoAFs em algumas circunstâncias raras.

Registrar os dados do LoAF(s) associados à interação INP permite que você tenha muito mais informações sobre a interação INP para ajudar a diagnosticá-la. Isso é muito útil para entender o atraso de entrada, já que você pode conferir quais outros scripts estavam sendo executados nesse frame.

Também pode ser útil entender a duração do processamento e o atraso da apresentação inexplicáveis se os manipuladores de eventos não reproduzirem os valores vistos para aqueles como outros scripts que estão em execução para seus usuários, o que pode não estar incluído no seu teste.

Não há nenhuma API direta para vincular uma entrada INP com sua entrada ou 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 uma (consulte o exemplo de script WhyNp).

A biblioteca web-vitals inclui todos os LoAFs interseccionados na propriedade longAnimationFramesEntries da interface de atribuição INP da v4.

Depois de vincular as entradas do LoAF, você pode incluir informações com a atribuição INP. O objeto scripts contém algumas das informações mais valiosas, já que pode mostrar o que mais estava sendo executado nesses frames. Assim, o beacon de volta desses dados ao seu serviço de análise permite que você entenda mais sobre por que as interações foram lentas.

Informar LoAFs para a interação da INP é uma boa maneira de descobrir quais são os problemas de interatividade mais urgentes em sua página. Cada usuário pode interagir de maneira diferente com sua página e, com um volume suficiente de dados de atribuição de INP, vários problemas em potencial serão incluídos nos dados de atribuição de INP. Isso permite que você classifique os scripts por volume para ver quais deles estão se correlacionando com INP lento.

Informar dados de animação mais longos de volta a um endpoint de análise

Uma desvantagem de analisar apenas os LoAFs da INP é que você pode perder outras áreas em potencial para melhorias que podem causar problemas futuros de INP. Isso pode levar a uma sensação de perseguição, em que você corrige um problema de INP esperando uma grande melhoria, mas descobre que a próxima interação mais lenta é apenas um pouco melhor do que isso. Por isso, seu INP não melhora muito.

Portanto, em vez de destacar a análise apenas para o INP LoAF, você pode considerar todos os LoAFs durante o ciclo de vida da página:

Uma página com muitos LoAFs, alguns dos quais acontecem durante interações mesmo que não sejam a interação da INP.
A análise de todos os LoAFs pode ajudar a identificar problemas futuros de INP.

No entanto, cada entrada do LoAF contém dados consideráveis, então você provavelmente não vai querer usá-los de volta. Em vez disso, você desejará restringir sua análise a alguns LoAFs ou alguns dados.

Alguns padrões sugeridos incluem:

Qual desses padrões funciona melhor para você depende do seu progresso na jornada de otimização e da duração dos frames de animação longos. Para um site que nunca foi otimizado para capacidade de resposta antes, pode haver muitos LoAFs que você pode limitar a apenas LoAFs com interações, definir um limite alto ou olhar apenas os piores. Ao resolver seus problemas comuns de capacidade de resposta, você pode ampliar isso não se limitando a apenas interações e diminuindo limites ou procurando padrões específicos.

Observar frames longos de animação com interações

Para ter insights além do frame longo de animação de INP, observe todos os LoAFs com interações, que podem ser detectados pela presença de um valor firstUIEventTimestamp.

Esse também pode ser um método mais fácil de monitorar LoAFs de INP em vez de tentar correlacionar os dois, o que pode ser mais complexo. Na maioria dos casos, isso inclui o INP LoAF de uma determinada visita e, em casos raros, ainda mostra interações longas que precisam ser corrigidas, já que podem ser a interação INP para outros usuários.

O código a seguir registra todas as entradas da LoAF superiores a 150 milissegundos em que uma interação ocorreu durante o frame. O 150 é escolhido aqui porque é um pouco menor do que o limite "bom" de INP de 200 milissegundos. Você pode escolher um valor maior ou menor, dependendo de 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 });

Observar frames de animação maiores que um determinado limite

Outra estratégia seria monitorar todos os LoAFs e sinalizar os que são maiores que um determinado limite de volta a 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 longas podem ser muito grandes, os desenvolvedores precisam decidir quais dados da entrada devem ser enviados para a análise. Por exemplo, os horários de resumo da entrada e talvez os nomes dos scripts ou algum outro conjunto mínimo de outros dados contextuais que possam ser considerados necessários.

Observar os piores frames de animação longos

Em vez de ter um limite definido, os sites podem coletar dados no frame (ou frames) de animação mais longo para reduzir o volume de dados que precisam ser beacons. Portanto, não importa quantos frames de animação longos sejam exibidos na página, somente os dados dos piores, cinco ou quantos frames de animação longos absolutamente necessários são retornados.

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 });

Essas estratégias também podem ser combinadas — analise apenas os 10 piores LoAFs, com interações que tenham mais de 150 milissegundos.

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

console.table(longestBlockingLoAFs);

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

Uma estratégia alternativa seria observar scripts comuns que aparecem mais em entradas de quadros de animação longas. Os dados podem ser informados no nível do script e da posição do personagem para identificar infratores reincidentes.

Isso pode funcionar muito bem em plataformas personalizáveis em que os temas ou plug-ins que causam problemas de desempenho podem ser identificados em vários sites.

O tempo de execução de scripts comuns (ou origens de terceiros) em frames de animação longos pode ser resumido e informado para identificar os colaboradores comuns de frames de animação longos em um site ou em uma coleção de sites. Por exemplo, observe os 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 o 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

Usar a API Long Animation Frames nas ferramentas

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

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

Você pode exibir frames de animação longos no DevTools usando a API performance.measure(), que são exibidas na faixa de velocidade do usuário do DevTools nos rastreamentos de desempenho para mostrar onde concentrar os 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 });

Em longo prazo, ele provavelmente será incorporado ao DevTools, mas o snippet de código anterior permite que ele seja exibido lá enquanto isso.

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

A extensão Web Vitals mostrou o valor de registrar informações de depuração resumidas para diagnosticar problemas de desempenho.

Agora, ele também exibe dados longos de frames de animação para cada callback de INP e cada interação:

Geração de registros do console da extensão de Métricas da Web.
A geração de registros do console da extensão de Métricas da Web mostra dados do LoAF.

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

Da mesma forma, ferramentas de teste automatizadas em pipelines de CI/CD podem mostrar detalhes sobre possíveis problemas de desempenho medindo frames longos de animação 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 maneira alternativa de relatar uma medida semelhante, mas essencialmente diferente, de possíveis problemas de capacidade de resposta. É importante garantir que os sites que dependem da API Long Tasks já existentes continuem funcionando para evitar a interrupção dos casos de uso atuais.

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

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

Preciso de feedback

É possível fornecer feedback na lista de problemas do GitHub (link em inglês) ou registrar bugs na implementação da API pelo Chrome no Issue Tracker do Chrome (links em inglês).

Conclusão

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

Ele é uma ferramenta fundamental para resolver problemas de capacidade de resposta, conforme medido pela INP. A INP é uma métrica difícil de otimizar, e essa API é uma das maneiras que a equipe do Chrome busca facilitar a identificação e a resolução de problemas para os desenvolvedores.

No entanto, o escopo da API Long Animation Frames vai além da 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.