Em um mundo de sombreadores, malhas e filtros, o Canvas2D pode não animar você. Mas deveria!
Entre 30% e 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, em geladeiras
e no espaço (sério).
É claro que a API está um pouco atrasada em relação aos desenhos 2D de última geração. Felizmente, trabalhamos muito para implementar novos recursos no Canvas2D e acompanhar o CSS, simplificar a ergonomia e melhorar a performance.
Parte 1: atualizar-se sobre o CSS
O CSS tem alguns comandos de desenho que estão faltando na Canvas2D. Com a nova API, adicionamos alguns dos recursos mais solicitados:
Retângulo arredondado
Retângulos arredondados: a base da Internet, da computação, da noite, da civilização.
Os retângulos arredondados são extremamente úteis: como botões, balões de chat, miniaturas, balões de fala, você escolhe. Sempre foi possível fazer um retângulo arredondado no Canvas2D. Isso fica 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 foi necessário para um retângulo arredondado simples e modesto:
Com a nova API, há um método roundRect()
.
ctx.roundRect(upper, left, width, height, borderRadius);
Assim, o exposto acima pode ser totalmente substituído por:
ctx.roundRect(10, 10, 200, 100, 20);
O método ctx.roundRect()
também recebe uma matriz para o argumento borderRadius
de até quatro
números. Esses raios controlam os quatro cantos do retângulo arredondado da mesma forma
que para CSS. Exemplo:
ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);
Confira a demonstração para brincar.
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 de Canvas2Ds ficaram muito atrasados. O Chrome adicionou vários novos atributos à renderização de texto do Canvas2D:
- ctx.letterSpacing
- ctx.wordSpacing
- ctx.fontVariant.
- ctx.fontKerning (link em inglês)
- ctx.fontStretch
- ctx.textDecoration
- ctx.textUnderlinePosition
- ctx.textRendering
Todos esses atributos correspondem aos atributos CSS com os mesmos nomes.
Parte 2: ajustes ergonômicos
Antes, algumas coisas com o Canvas2D eram possíveis, mas a implementação era desnecessariamente complicada. Confira algumas melhorias na qualidade de vida para desenvolvedores de JavaScript que querem usar o Canvas2D:
Redefinição de contexto
Para explicar a limpeza de uma tela, escrevi uma pequena função para desenhar um padrão retrô:
draw90sPattern();
Ótimo! Agora que terminei esse padrão, quero limpar a tela e desenhar outra coisa.
Como limpar uma tela? Boa! ctx.clearRect()
, é claro.
ctx.clearRect(0, 0, canvas.width, canvas.height);
Não funcionou. Boa! Primeiro, preciso redefinir a transformação:
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
Perfeito! Uma tela em branco. Agora vamos começar a desenhar uma boa linha horizontal:
ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();
Grrrr! Isso não está certo. 😡 O que essa linha extra está fazendo aqui? Além disso, por que ele está rosa? Ok, vamos verificar o StackOverflow.
canvas.width = canvas.width;
Por que isso é tão? Por que está tão difícil?
Agora não é mais. Com a nova API, temos uma API simples, elegante e inovadora:
ctx.reset();
Lamentamos a demora.
Filtros
Os filtros SVG são um mundo à parte. Se eles são novos para você, recomendamos ler The Art Of SVG Filters And why It Is Awesome, que mostra o incrível potencial deles.
Os filtros de estilo SVG já estão disponíveis para o Canvas2D. Você só precisa 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 atrapalha bastante o nosso padrão:
Mas e se você quiser fazer o que está acima, mas permanecer no JavaScript e não mexer com 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 },
]);
Simples assim. Teste e brinque com os parâmetros na demonstração aqui.
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 oferecer aos desenvolvedores um controle mais preciso dos sites e permitir as melhores taxas de frames possíveis:
Vai ler com frequência
Use getImageData()
para ler os dados de pixels de uma tela. Isso 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 as coisas em segundo plano e manter a tela rápida para uma variedade maior de casos de
uso. Esse recurso já está no Firefox há algum tempo e agora faz parte da especificação da
tela.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
Perda de contexto
Vamos deixar guias tristes felizes outra vez! Caso um cliente fique sem memória da GPU ou algum outro problema ocorra na tela, agora é possível receber um callback e redesenhar conforme necessário:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);
Se você quiser ler mais sobre o contexto e a perda da tela, o WhatWG tem uma boa explicação (em inglês) na wiki.
Conclusão
Não importa se você é novo no Canvas2D, se já o usa há anos ou se evita usá-lo há anos, dê outra olhada no canvas. É a API vizinha que estava lá o tempo todo.