Em um mundo de sombreadores, mesh e filtros, o Canvas2D pode não animar você. Mas deveria acontecer!
30% a 40% das páginas da Web têm um elemento <canvas>
, e 98% de todas as telas usam um contexto de renderização
Canvas2D. Há Canvas2Ds em carros, geladeiras
e no espaço (realmente).
É certo que a API está um pouco atrasada quando se trata de desenhos 2D de última geração. Felizmente, estamos trabalhando muito na implementação de novos recursos no Canvas2D para acompanhar o CSS, agilizar a ergonomia e melhorar o desempenho.
Parte 1: atualização com o CSS
O CSS tem alguns comandos de desenho que faltam muito no Canvas2D. Com a nova API, adicionamos vários dos recursos mais solicitados:
Retângulo arredondado
Retângulos arredondados: a base da Internet, da computação, perto da civilização.
Falando sério, retângulos arredondados são extremamente úteis: como botões, balões de chat, miniaturas, balões de diálogo, entre outros. Sempre foi possível fazer um retângulo arredondado no Canvas2D, mas ficava um pouco confuso:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'magenta';
const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;
ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(left + width, top + height, left + width - radius, top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius);
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius);
ctx.stroke();
Tudo isso era necessário para um retângulo arredondado simples:
Com a nova API, há um método roundRect()
.
ctx.roundRect(upper, left, width, height, borderRadius);
Portanto, o código acima pode ser totalmente substituído por:
ctx.roundRect(10, 10, 200, 100, 20);
O método ctx.roundRect()
também aceita uma matriz para o argumento borderRadius
de até quatro números. Esses raios controlam os quatro cantos do retângulo arredondado da mesma maneira
que em CSS. Exemplo:
ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);
Gradiente cônico
Você já viu gradientes lineares:
const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'magenta');
gradient.addColorStop(1, 'white');
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);
Gradientes radiais:
const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, 'white');
radialGradient.addColorStop(0.5, 'magenta');
radialGradient.addColorStop(1, 'lightblue');
ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
Mas que tal um bom gradiente cônico?
const grad = ctx.createConicGradient(0, 100, 100);
grad.addColorStop(0, 'red');
grad.addColorStop(0.25, 'orange');
grad.addColorStop(0.5, 'yellow');
grad.addColorStop(0.75, 'green');
grad.addColorStop(1, 'blue');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);
Modificadores de texto
Os recursos de renderização de texto do Canvas2Ds estavam lamentáveis. O Chrome adicionou vários novos atributos à renderização de texto Canvas2D:
- ctx.letterSpacing
- ctx.wordSpacing
- ctx.fontVariant
- ctx.fontKerning
- ctx.fontStretch
- ctx.textDecoration
- ctx.textUnderlinePosition
- ctx.textRendering
Todos esses atributos correspondem às contrapartes do CSS com os mesmos nomes.
Parte 2: ajustes ergonômicos
Anteriormente, algumas coisas eram possíveis com o Canvas2D, mas desnecessariamente complicadas de implementar. Confira algumas melhorias na qualidade de vida para desenvolvedores de JavaScript que querem usar o Canvas2D:
Redefinição de contexto
Para explicar como limpar uma tela, escrevi uma pequena função para desenhar um padrão retrô (link em inglês):
draw90sPattern();
Ótimo! Agora que terminei esse padrão, quero limpar a tela e desenhar outra coisa.
Como podemos limpar uma tela de novo? Fui, sim! ctx.clearRect()
, é claro.
ctx.clearRect(0, 0, canvas.width, canvas.height);
Ah... isso não funcionou. Fui, sim! Primeiro, preciso redefinir a transformação:
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
Perfeito! Uma boa tela em branco. Agora vamos começar a desenhar uma linha horizontal bem legal:
ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();
Grrrr! Incorreto! 📔 O que essa linha extra faz aqui? E por que é rosa? Certo, vamos conferir o StackOverflow.
canvas.width = canvas.width;
Por que isso é tão bobeiro? Por que isso é tão difícil?
Bem, não é mais nada. Com a nova API, temos a inovação simples, elegante e bonita:
ctx.reset();
Lamentamos a demora.
Filtros
Os filtros SVG são um mundo todo só. Se esse não for seu caso, recomendo que você leia The Art Of SVG Filters And Why It Is Awesome, que mostra um pouco do potencial incrível deles.
Os filtros de estilo SVG já estão disponíveis para o Canvas2D. Basta estar disposto a transmitir o filtro como um URL que aponta para outro elemento de filtro SVG na página:
<svg>
<defs>
<filter id="svgFilter">
<feGaussianBlur in="SourceGraphic" stdDeviation="5" />
<feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3" />
<feColorMatrix type="hueRotate" values="90" />
</filter>
</defs>
</svg>
const canvas = document.createElement('canvas');
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);
O que estraga muito nosso padrão:
Mas, e se você quisesse fazer o que foi descrito acima e permanecer no JavaScript sem mexer nas strings? Com a nova API, isso é totalmente possível.
ctx.filter = new CanvasFilter([
{ filter: 'gaussianBlur', stdDeviation: 5 },
{
filter: 'convolveMatrix',
kernelMatrix: [
[-3, 0, 0],
[0, 0.5, 0],
[0, 0, 3],
],
},
{ filter: 'colorMatrix', type: 'hueRotate', values: 90 },
]);
Superfácil! Teste e teste com os parâmetros nesta demonstração.
Parte 3: melhorias de desempenho
Com a nova API Canvas2D, também queríamos melhorar o desempenho sempre que possível. Adicionamos alguns recursos para dar aos desenvolvedores controle mais preciso dos sites e permitir os melhores frames possíveis:
Lerá com frequência
Use getImageData()
para ler dados de pixels de uma tela. Pode ser muito lento. A nova API oferece
uma maneira de marcar explicitamente uma tela para leitura (para efeitos generativos, por exemplo).
Isso permite otimizar internamente e manter a tela rápida para uma variedade maior de casos de
uso. Esse recurso já está no Firefox há algum tempo e finalmente estamos finalmente tornando-o parte da especificação
do canvas.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
Perda de contexto
Vamos deixar guias tristes felizes de novo! Caso um cliente fique sem memória de GPU ou algum outro desastre ocorra na tela, você pode receber um callback e desenhar novamente conforme necessário:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);
Se você quiser saber mais sobre contexto e perda da tela, o WhatWG tem uma boa explicação (link em inglês) no wiki deles.
Conclusão
Não importa se você é iniciante, se usa há anos ou se evita usá-lo há anos, estou aqui para dizer para você dar outra olhada no canvas. É a API próxima que está lá o tempo todo.
Agradecimentos
Imagem principal de Sandie Clarke no Unsplash (links em inglês).