En un mundo de sombreadores, mallas y filtros, es posible que Canvas2D no te entusiasme. Pero debería.
Entre el 30 y el 40% de las páginas web tienen un elemento <canvas>
, y el 98% de todos los lienzos usan un contexto de renderización Canvas2D. Hay Canvas2D en automóviles, refrigeradores, y en el espacio (en serio).
Es cierto que la API está un poco desactualizada en lo que respecta al dibujo 2D de última generación. Por suerte, trabajamos arduamente para implementar nuevas funciones en Canvas2D para ponernos al día con CSS, optimizar la ergonomía y mejorar el rendimiento.
Parte 1: Actualiza tu conocimiento sobre CSS
CSS tiene algunos comandos de dibujo que faltan mucho en Canvas2D. Con la nueva API, agregamos algunas de las funciones más solicitadas:
Rectángulo redondo
Rectángulos redondeados: la piedra angular de Internet, de la informática, de la civilización.
En serio, los rectángulos redondeados son muy útiles: como botones, burbujas de chat, miniaturas, cuadros de diálogo, lo que sea. Siempre fue posible hacer un rectángulo redondeado en Canvas2D, solo que era un poco desordenado:
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();
Todo esto era necesario para un rectángulo redondeado modesto y simple:
Con la nueva API, hay un método roundRect()
.
ctx.roundRect(upper, left, width, height, borderRadius);
Por lo tanto, lo anterior se puede reemplazar por completo por lo siguiente:
ctx.roundRect(10, 10, 200, 100, 20);
El método ctx.roundRect()
también toma un array para el argumento borderRadius
de hasta cuatro números. Estos radios controlan las cuatro esquinas del rectángulo redondeado de la misma manera que CSS. Por ejemplo:
ctx.roundRect(10, 10, 200, 100, [15, 50, 30]);
Mira la demostración para probarla.
Gradiente cónico
Ya viste los gradientes lineales:
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 radiales:
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);
Pero ¿qué tal un buen 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
Las capacidades de renderización de texto de Canvas2Ds quedaron muy rezagadas. Chrome agregó varios atributos nuevos a la renderización de texto de Canvas2D:
- ctx.letterSpacing
- ctx.wordSpacing
- ctx.fontVariant
- ctx.fontKerning
- ctx.fontStretch
- ctx.textDecoration
- ctx.textUnderlinePosition
- ctx.textRendering
Todos estos atributos coinciden con sus contrapartes de CSS con los mismos nombres.
Parte 2: ajustes ergonómicos
Anteriormente, era posible hacer algunas cosas con Canvas2D, pero su implementación era innecesariamente complicada. Estas son algunas mejoras de calidad de vida para los desarrolladores de JavaScript que quieran usar Canvas2D:
Restablecimiento del contexto
Para explicar cómo borrar un lienzo, escribí una pequeña función para dibujar un patrón retro:
draw90sPattern();
¡Genial! Ahora que terminé con ese patrón, quiero borrar el lienzo y dibujar algo más.
Espera, ¿cómo volvemos a borrar un lienzo? ¡Por supuesto! ctx.clearRect()
, por supuesto.
ctx.clearRect(0, 0, canvas.width, canvas.height);
No funcionó. ¡Por supuesto! Primero, debo restablecer la transformación:
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);

¡Perfecto! Un buen lienzo en blanco. Ahora comencemos a dibujar una línea horizontal bonita:
ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();
¡Grrrr! Eso no es correcto. 😡 ¿Qué hace esa línea adicional aquí? Además, ¿por qué es rosa? De acuerdo, vamos a revisar StackOverflow.
canvas.width = canvas.width;
¿Por qué es tan? ¿Por qué es tan difícil?
Bueno, ya no. Con la nueva API, tenemos lo siguiente:
ctx.reset();
Lo siento, tardamos tanto.
Filtros
Los filtros SVG son un mundo aparte. Si es la primera vez que los usas, te recomiendo que leas The Art Of SVG Filters And Why It Is Awesome, que muestra parte de su increíble potencial.
Los filtros de estilo SVG ya están disponibles para Canvas2D. Solo debes pasar el filtro como una URL que apunte a otro elemento de filtro de SVG en la 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);
Lo que desordena bastante nuestro patrón:
Pero, ¿qué sucede si quieres hacer lo anterior, pero permanecer en JavaScript y no jugar con cadenas? Con la nueva API, esto es totalmente posible.
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 },
]);
Es muy fácil. Pruébala y juega con los parámetros en la demostración aquí.
Parte 3: Mejoras en el rendimiento
Con la nueva API de Canvas2D, también queríamos mejorar el rendimiento siempre que fuera posible. Agregamos algunas funciones para brindarles a los desarrolladores un control más detallado de sus sitios web y permitir las velocidades de fotogramas más fluidas posibles:
Se leerá con frecuencia
Usa getImageData()
para volver a leer datos de píxeles desde un lienzo. Puede ser muy lento. La nueva API te brinda una forma de marcar explícitamente un lienzo para volver a leerlo (por ejemplo, para efectos generativos).
Esto te permite optimizar los elementos internos y mantener el lienzo rápido para una mayor variedad de casos de uso. Esta función está disponible en Firefox desde hace un tiempo y, por fin, la incorporaremos a la especificación del lienzo.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
Pérdida de contexto
¡Hagamos que las pestañas tristes vuelvan a ser felices! En caso de que un cliente se quede sin memoria de GPU o se produzca algún otro desastre en tu lienzo, ahora puedes recibir una devolución de llamada y volver a dibujar según sea necesario:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.addEventListener('contextlost', onContextLost);
canvas.addEventListener('contextrestored', redraw);
Si quieres obtener más información sobre el contexto y la pérdida del lienzo, WHATWG tiene una buena explicación en su wiki.
Conclusión
Ya sea que sea la primera vez que usas Canvas2D, lo hayas usado durante años o lo hayas evitado durante años, te recomiendo que le des otra oportunidad. Es la API de al lado que estuvo allí todo el tiempo.