OffscreenCanvas: agilize suas operações com um web worker

Tim Dresser

O Canvas é uma forma conhecida de de desenhar todos os tipos de gráficos na tela e um ponto de entrada para o mundo do WebGL. Ele pode ser usado para desenhar formas, imagens, executar animações ou até mesmo exibir e processar conteúdo de vídeo. Ele é frequentemente usado para criar belas experiências do usuário em aplicativos da Web com rich media jogos on-line.

É scriptável, ou seja, o conteúdo desenhado na tela pode ser criado de forma programática, por exemplo, em JavaScript. Isso dá à tela grande flexibilidade.

Ao mesmo tempo, em sites modernos, a execução de script é uma das mais frequentes fontes de problemas de capacidade de resposta do usuário. Como a lógica e a renderização da tela acontecem na mesma linha de execução da interação do usuário, os cálculos (às vezes pesados) envolvidos nas animações podem prejudicar e o desempenho percebido.

Felizmente, OffscreenCanvas é uma resposta a essa ameaça.

Compatibilidade com navegadores

  • 69
  • 79
  • 105
  • 16.4

Origem

Anteriormente, os recursos de desenho da tela estavam vinculados ao elemento <canvas>. o que significava que ela dependia diretamente do DOM. OffscreenCanvas, como o nome indica, dissocia o DOM da API Canvas movendo-o para fora da tela.

Graças a essa separação, a renderização de OffscreenCanvas é totalmente independente do DOM oferece algumas melhorias de velocidade em relação à tela normal, já que não há sincronização entre os dois.

Além disso, ele pode ser usado por um Web Worker, mesmo que não haja DOM disponível. Isso permite todos os tipos de casos de uso interessantes.

Usar o OffscreenCanvas em um worker

Workers são a versão das linhas de execução da Web, que permitem a execução de tarefas em segundo plano.

Mover alguns scripts para um worker permite que o app tenha mais espaço para realizar tarefas essenciais aos usuários, tarefas na linha de execução principal. Sem o OffscreenCanvas, não seria possível usar a API Canvas em um worker porque não havia DOM disponível.

O OffscreenCanvas não depende do DOM e, por isso, pode ser usado. O exemplo a seguir usa OffscreenCanvas para calcular uma cor de gradiente em um worker:

// file: worker.js
function getGradientColor(percent) {
  const canvas = new OffscreenCanvas(100, 1);
  const ctx = canvas.getContext('2d');
  const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
  gradient.addColorStop(0, 'red');
  gradient.addColorStop(1, 'blue');
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, ctx.canvas.width, 1);
  const imgd = ctx.getImageData(0, 0, ctx.canvas.width, 1);
  const colors = imgd.data.slice(percent * 4, percent * 4 + 4);
  return `rgba(${colors[0]}, ${colors[1]}, ${colors[2]}, ${colors[3]})`;
}

getGradientColor(40);  // rgba(152, 0, 104, 255 )

Desbloquear conversa principal

Mover cálculos pesados para um worker permite que você libere recursos significativos na linha de execução principal. Usar o método transferControlToOffscreen para espelhar a tela normal em uma instância de OffscreenCanvas. As operações aplicadas a OffscreenCanvas será renderizado na tela de origem automaticamente.

const offscreen = document.querySelector('canvas').transferControlToOffscreen();
const worker = new Worker('myworkerurl.js');
worker.postMessage({canvas: offscreen}, [offscreen]);

No exemplo a seguir, o cálculo pesado acontece quando o tema de cores está mudando. Deve levam alguns milésimos de segundo, mesmo em um computador rápido. Você pode executar animações na linha de execução principal ou no worker. No caso da linha de execução principal, não é possível interagir com o botão enquanto a carga está em execução, a linha de execução está bloqueada. No caso do worker, não há impacto sobre Capacidade de resposta da interface.

Demonstração

Também funciona de forma inversa: a linha de execução principal ocupada não influencia a animação em execução. um worker. Use esse recurso para evitar instabilidade visual e garantir uma animação suave. apesar do tráfego da linha de execução principal, como mostrado na demonstração a seguir.

Demonstração

No caso de uma tela normal, a animação é interrompida quando a linha de execução principal fica artificialmente sobrecarregada. enquanto o OffscreenCanvas é executado sem problemas.

Como a API OffscreenCanvas geralmente é compatível com o elemento Canvas normal, é possível usá-lo como um aprimoramento progressivo, também com algumas das principais bibliotecas de gráficos do mercado.

Por exemplo, é possível detectá-lo com recursos e, se disponível, usá-lo com o Three.js especificando a opção "canvas" no construtor do renderizador:

const canvasEl = document.querySelector('canvas');
const canvas =
  'OffscreenCanvas' in window
    ? canvasEl.transferControlToOffscreen()
    : canvasEl;
canvas.style = {width: 0, height: 0};
const renderer = new THREE.WebGLRenderer({canvas: canvas});

O ponto aqui é que o Three.js espera que a tela tenha uma propriedade style.width e style.height. O OffscreenCanvas, independente do DOM, não o tem, então você precisa fornecê-lo por conta própria, fragmentando-os ou fornecendo uma lógica que vincule esses valores ao original dimensões da tela.

Confira a seguir como executar uma animação básica do three.js em um worker:

Demonstração

Tenha em mente que algumas das APIs relacionadas ao DOM não estão prontamente disponíveis em um worker. quiser usar recursos mais avançados do Three.js, como texturas, pode precisar de mais soluções alternativas. Para obter algumas ideias sobre como começar a usá-los, consulte a vídeo do Google I/O 2017 (em inglês).

Se você está usando os recursos gráficos do canvas, o OffscreenCanvas pode trazer influenciam o desempenho do app. Tornar os contextos de renderização de tela disponíveis para os workers aumenta paralelismo em aplicativos da Web e faz melhor uso de sistemas com vários núcleos.

Outros recursos