Os aplicativos de desenho baseados em stylus criados para a Web sempre sofreram com problemas de latência, porque uma página da Web precisa sincronizar atualizações gráficas com o DOM. Em qualquer aplicativo de desenho, latências maiores que 50 milissegundos podem interferir na coordenação mão-olho do usuário, dificultando o uso dos aplicativos.
A sugestão desynchronized
para canvas.getContext()
invoca um caminho de código
diferente que ignora o mecanismo de atualização DOM usual.
Em vez disso, a dica informa ao sistema subjacente para pular o máximo possível de
composição. Em alguns casos, o buffer subjacente da tela é enviado diretamente para
o controlador de exibição da tela. Isso elimina a latência que seria
causada pelo uso da fila do compositor do renderizador.
O que você achou?
Se você quiser acessar o código, role a tela para baixo. Para conferir em ação, você precisa de um dispositivo com tela touchscreen e, de preferência, uma stylus. Os dedos também funcionam. Se você tiver um, tente as amostras 2d ou webgl. Para os demais, confira esta demonstração de Miguel Casas, um dos engenheiros que implementaram esse recurso. Abra a demonstração, pressione "Play" e mova o controle deslizante para frente e para trás de forma aleatória e rápida.
Este exemplo usa um clipe de um minuto e 21 segundos do curta-metragem
Sintel de Durian, o projeto de filme aberto do Blender. Neste exemplo, o filme é reproduzido em um elemento <video>
cujo
conteúdo é renderizado simultaneamente em um elemento <canvas>
. Muitos dispositivos podem
fazer isso sem rasgos, embora dispositivos com renderização de buffer frontal, como
ChromeOS, por exemplo, possam ter rasgos. O filme é ótimo, mas de partir o coração.
Fiquei inútil por uma hora depois de ver isso. Considere isso um aviso.)
Como usar a dica
Há mais coisas para usar a baixa latência do que adicionar desynchronized
a
canvas.getContext()
. Vou analisar os problemas um por um.
Criar a tela
Em outra API, eu discutiria a detecção de recursos primeiro. Para a dica desynchronized
,
você precisa criar a tela primeiro. Chame canvas.getContext()
e transmita
a nova sugestão desynchronized
com um valor de true
.
const canvas = document.querySelector('myCanvas');
const ctx = canvas.getContext('2d', {
desynchronized: true,
// Other options. See below.
});
Detecção de recursos
Em seguida, chame getContextAttributes()
. Se o objeto de atributos retornado tiver uma
propriedade desynchronized
, teste-a.
if (ctx.getContextAttributes().desynchronized) {
console.log('Low latency canvas supported. Yay!');
} else {
console.log('Low latency canvas not supported. Boo!');
}
Como evitar oscilações
Há duas instâncias em que você pode causar oscilação se não programar corretamente.
Alguns navegadores, incluindo o Chrome, limpam as telas WebGL entre os frames. É
possível que o controlador de exibição leia o buffer enquanto ele está vazio, fazendo com
que a imagem desenhada pisque. Para evitar isso, defina
preserveDrawingBuffer
como true
.
const canvas = document.querySelector('myCanvas');
const ctx = canvas.getContext('webgl', {
desynchronized: true,
preserveDrawingBuffer: true
});
O flicker também pode ocorrer quando você limpa o contexto da tela no seu próprio código de exibição. Se for necessário limpar, desenhe em um framebuffer fora da tela e copie para a tela.
Canais Alfa
Um elemento de tela translúcida, em que o alfa é definido como "true", ainda pode ser desincronizado, mas não pode ter outros elementos DOM acima dele.
Só pode haver um
Não é possível mudar os atributos de contexto após a primeira chamada para
canvas.getContext()
. Isso sempre foi verdade, mas repetir isso pode evitar
frustração se você não souber ou se tiver esquecido .
Por exemplo, digamos que eu receba um contexto e especifique alpha como falso. Em algum momento mais tarde no código, chamo canvas.getContext()
uma segunda vez com alpha definido como verdadeiro, conforme mostrado abaixo.
const canvas = document.querySelector('myCanvas');
const ctx1 = canvas.getContext('2d', {
alpha: false,
desynchronized: true,
});
//Some time later, in another corner of code.
const ctx2 = canvas.getContext('2d', {
alpha: true,
desynchronized: true,
});
Não é óbvio que ctx1
e ctx2
são o mesmo objeto. O Alfa ainda é falso, e um
contexto com Alfa igual a "true" nunca é criado.
Tipos de tela compatíveis
O primeiro parâmetro transmitido para getContext()
é o contextType
. Se você já
conhece getContext()
, provavelmente está se perguntando se há suporte para
outros tipos de contexto além do 2D. A tabela abaixo mostra os tipos de
contexto compatíveis com desynchronized
.
contextType | Objeto do tipo de contexto |
---|---|
|
|
|
|
|
|
Conclusão
Se quiser saber mais sobre isso, confira as amostras. Além do exemplo de vídeo já descrito, há exemplos que mostram os contextos 2D e WebGL.