Canvas HTML5: Fundamentos
Mihai Sucan. 8 de enero de 2009. Publicado en: imágenes, texto, gráficos, 2D, gradientes, html5, web abierta, canvas
Introducción
La especificación de HTML5 incluye un montón de nuevas características, una de las cuales es el elemento canvas
(literalmente, lienzo). Los canvas
de HTML5 proporcionan una manera fácil y potente de dibujar gráficos usando JavaScript. Para cada elemento canvas
puedes utilizar un "contexto" (piensa en una página de un cuaderno de dibujo), en el que puedes lanzar comandos JavaScript para dibujar lo que quieras. Los navegadores pueden aplicar múltiples contextos canvas y las diferentes APIs proporcionan la funcionalidad de dibujo.
La mayoría de los navegadores más importantes incluyen la capacidad de contextos canvas 2D: Opera, Firefox, Konqueror y Safari. Además, hay versiones experimentales de Opera que incluyen soporte para contextos canvas 3D, y un add-on que habilita el soporte para canvas 3D en Firefox:
- Descarga una versión de Opera con canvas 3D, vídeo HTML y soporte para operar con archivos.
- Para saber más sobre el uso del contexto lienzo 3D en Opera.
- Para saber más acerca de cómo obtener y usar el contexto canvas 3D de Firefox.
En este artículo veremos los fundamentos de la aplicación de un contexto canvas 2D, y cómo utilizar las funciones básicas de canvas, incluyendo rectas, formas simples, imágenes, texto y mucho más. Supondremos que ya dominas los fundamentos de JavaScript.
Ten en cuenta que puedes descargar todos los ejemplos de código en un solo archivo zip, así como verlos en vivo usando los enlaces que incluimos.
Las bases del uso de canvas
Crear un contexto canvas en una página es tan fácil como añadir el elemento <canvas>
al documento HTML así:
<canvas id="myCanvas" width="300" height="150">
Contenido para el caso en que el navegador no soporte Canvas.
</canvas>
Es necesario definir un ID de elemento para poder localizar después el elemento en el código JavaScript, y también es necesario definir una anchura y una altura para el lienzo.
Ya hemos creado el cuaderno de dibujo, o sea que toca llevar el lápiz al papel. Para dibujar en el canvas hay que utilizar JavaScript. Primero se localiza el elemento canvas con getElementById
y, a continuación, se inicializa el contexto que desees. Una vez hecho esto, puedes empezar a dibujar en el canvas con los comandos disponibles en la API del contexto. La siguiente secuencia de comandos (ejecuta el ejemplo en vivo) dibuja un simple rectángulo en el canvas definido anteriormente:
// Get a reference to the element.
var elem = document.getElementById('myCanvas');
// Always check for properties and methods, to make sure your code doesn't break
// in other browsers.
if (elem && elem.getContext) {
// Get the 2d context.
// Remember: you can only initialize one context per element.
var context = elem.getContext('2d');
if (context) {
// You are done! Now you can draw your first rectangle.
// You only need to provide the (x,y) coordinates, followed by the width and
// height dimensions.
context.fillRect(0, 0, 150, 100);
}
}
Puedes elegir entre incluir el script en el head del documento o en un fichero externo.
La API de contexto 2D
Ya hemos creado nuestra primera imagen canvas básica. Echemos ahora una ojeada un poco más profunda a la API 2D de canvas, y veamos qué tenemos a nuestra disposición.
Segmentos y trazos básicos
Ya hemos visto en el ejemplo anterior que es muy fácil dibujar rectángulos de color a nuestro gusto.
Con las propiedades fillStyle
y strokeStyle
puedes elegir fácilmente los colores utilizados para crear formas y trazos básicos. Los valores de color que puedes utilizar son los mismos que en CSS: códigos hexadecimales, RGB(), rgba() e incluso HSLA(), si el navegador es compatible (por ejemplo, esta función es soportada por Opera 10.0 y versiones posteriores).
Con fillRect
puedes dibujar rectángulos rellenos. Con strokeRect
puedes dibujar solo los bordes del rectángulo, sin rellenar. Si deseas borrar una parte del canvas, puedes utilizar clearRect. Los tres métodos usan los mismos argumentos: x
, y
, width
, height
. Los dos primeros argumentos dan las coordenadas (x, y) y los dos últimos dan las dimensiones de anchura y altura del rectángulo.
Para cambiar el grosor de las líneas puedes utilizar la propiedad lineWidth
. Veamos un ejemplo que utiliza clearRect
, fillRect
, strokeRect
y más:
context.fillStyle = '#00f'; // azul
context.strokeStyle = '#f00'; // rojo
context.lineWidth = 4;
// Dibujar unos cuantos rectángulos.
context.fillRect (0, 0, 150, 50);
context.strokeRect(0, 60, 150, 50);
context.clearRect (30, 25, 90, 60);
context.strokeRect(30, 25, 90, 60);
Este ejemplo produce un resultado como el que se puede fer en la Figura 1.
Figura 1: Ejemplo de fillRect
, strokeRect
y clearRect
.
Caminos
Los caminos (paths) de canvas permiten dibujar formas personalizadas. Primero se dibuja el "perfil", a continuación se dibujan los trazos y finalmente se rellena la forma, si se desea. Crear una forma personalizada es simple: para empezar a dibujar usas beginPath()
y a continuación trazas el camino que compone la forma mediante líneas, curvas y otras primitivas. Una vez has terminado, usas fill
y stroke
si quieres para rellenar la forma o dibujar un trazo y, finalmente, usas closepath()
para acabar la forma.
Toca un ejemplo. El siguiente código dibuja un triángulo:
// Establece las propiedades de estilo.
context.fillStyle = '#00f';
context.strokeStyle = '#f00';
context.lineWidth = 4;
context.beginPath();
// Comenzar desde el punto de arriba a la izquierda.
context.moveTo(10, 10);
// se dan las coordenadas (x,y)
context.lineTo(100, 10);
context.lineTo(10, 100);
context.lineTo(10, 10);
// Listo! Ahora se rellena la forma y dibuja la línea.
// Nota: la forma no será visible hasta que llames uno de los dos métodos.
context.fill();
context.stroke();
context.closePath();
Esto genera algo como lo que se muestra en la Figura 2.
Figura 2: Un triángulo básico
También he preparado un ejemplo de rutas más complejo, con rectas, curvas y arcos. Échale un vistazo.
Insertar imágenes
El método drawImage
permite insertar otras imágenes (elementos img
y canvas
) en el contexto de tu canvas. En Opera también se pueden dibujar imágenes SVG dentro de un canvas. Se trata de un método bastante complejo, que puede tomar tres, cinco o nueve argumentos:
- Tres argumentos: El uso básico de
drawImage
consiste en un argumento para apuntar a la imagen a incluir y dos para especificar las coordenadas de destino dentro del contexto canvas. - Cinco argumentos: El uso medio de
drawImage
incluye esos tres argumentos, más dos para especificar la anchura y la altura de la imagen insertada (en caso de que se desee cambiar su tamaño). - Nueve argumentos: El uso más avanzado de
drawImage
incluye los anteriores cinco argumentos, más dos valores de coordenadas dentro de las imágenes de origen y dos valores para la anchura y la altura dentro de la imagen de origen. Estos valores permiten recortar de forma dinámica la imagen de origen antes de ponerla en el contexto canvas.
El siguiente código de ejemplo muestra los tres tipos de drawImage en acción:
// 3 argumentos: elemento y coordenadas (x,y) de destino.
context.drawImage(img_elem, dx, dy);
// 5 argumentos: elemento, coordenadas (x,y) de destino y
// anchura y altura (si quieres cambiarle el tamaño a la imagen).
context.drawImage(img_elem, dx, dy, dw, dh);
// 9 argumentos: el elemento, coordenadas (x,y) de la fuente, anchura y
// altura de la fuente (para recortar), coordenadas (x,y) de destino y anchura y
// altura de destino (reescalado).
context.drawImage(img_elem, sx, sy, sw, sh, dx, dy, dw, dh);
Esto debería generar lo que se muestra en la Figura 3.
Figura 3: Ejemplo de drawImage
Manipulación basada en píxels
La API del contexto 2D ofrece tres métodos para dibujar píxel a píxel: createImageData
, getImageData
y putImageData
.
Los píxeles en bruto se guardan en objetos de tipo ImageData
. Cada objeto tiene tres propiedades: width
, height
y data
. La propiedad data
es de tipo CanvasPixelArray
, y almacena una cantidad de elementos igual a width*height*4
, lo que significa que para cada píxel se definen los valores de rojo, verde, azul y alfa, en el orden en que quieres que aparezcan (todos los valores van de 0 a 255, incluyendo el alfa). Los píxeles se ordenan de izquierda a derecha, fila por fila, de arriba a abajo.
Para entender mejor cómo funciona todo esto, echa un vistazo a un ejemplo que dibuja un bloque de píxeles de color rojo.
// Crea un objeto ImageData.
var imgd = context.createImageData(50,50);
var pix = imgd.data;
// Bucle sobre cada píxel y poner a rojo.
for (var i = 0; n = pix.length, i < n; i += 4) {
pix[i ] = 255; // red channel
pix[i+3] = 127; // alpha channel
}
// Dibujar el objeto ImageData en las coordenadas (x,y) dadas.
context.putImageData(imgd, 0,0);
Nota: No todos los navegadores han implementado createImageData
. En los navegadores sin soporte es necesario obtener el objeto ImageData
utilizando el método getImageData
. Por favor, consulta el código de ejemplo proporcionado.
Con las capacidades de ImageData
se puede hacer mucho más que esto. Por ejemplo, se puede hacer filtrado de imágenes, o visualizaciones matemáticas (como fractales y más). El código siguiente muestra cómo crear un filtro simple de inversión de color:
// Obener el CanvasPixelArray de las coordenadas y dimensiones dadas.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;
// Bucle sobre cada píxel e invertir color.
for (var i = 0, n = pix.length; i < n; i += 4) {
pix[i ] = 255 - pix[i ]; // rojo
pix[i+1] = 255 - pix[i+1]; // verde
pix[i+2] = 255 - pix[i+2]; // azul
// i+3 es el alfa (el cuarto elemento)
}
// Dibujar ImageData en las coordenadas (x,y) dadas.
context.putImageData(imgd, x, y);
La figura 4 muestra el filtro de inversión de color aplicado a un gráfico de Opera (compara con la figura 3, que muestra el esquema de color original del gráfico de Opera).
Figura 4: el filtro de inversión de color en acción
Texto
La API de texto sólo está disponible sólo en versiones recientes de WebKit y en las compilaciones de prueba (‘nightlies’) de Firefox 3.1, pero decidí incluirla aquí por completitud1.
Las siguientes propiedades de texto están disponibles en el objeto context:
font
: Especifica el tipo de letra del texto, de la misma manera que la propiedad CSSfont-family
.textAlign
: Especifica la alineación horizontal del texto. Valores:start
,end
,left
,right
,center
. Valor por defecto:start
.textBaseline
: Especifica la alineación vertical del texto. Valores:top
,hanging
,middle
,alphabetic
,ideographic
,bottom
. Valor por defecto:alphabetic
.
Hay dos métodos para escribir texto: fillText
y strokeText
. El primero da forma al texto y lo rellena con el fillStyle
actual, mientras que el segundo dibuja el perfil/borde del texto con el strokeStyle
actual. Ambos toman tres argumentos: el texto que desea mostrar, y las coordenadas (x, y) para definir dónde mostrarlo. También hay un cuarto argumento opcional: anchura máxima. Esto hace que el navegador reduzca el tamaño del texto para que quepa en el ancho dado, si es necesario.
Las propiedades de alineación del texto afectan a la posición del texto en relación con las coordenadas (x, y) que pases a los métodos de dibujo.
En este punto toca un ejemplo. El siguiente código es un ejemplo simple de texto canvas "hola mundo".
context.fillStyle = '#00f';
context.font = 'italic 30px sans-serif';
context.textBaseline = 'top';
context.fillText ('Hello world!', 0, 0);
context.font = 'bold 30px sans-serif';
context.strokeText('Hello world!', 0, 50);
La figura 5 muestra lo que crea el ejemplo.
Figura 5: un ejemplo sencillo de texto en canvas
Sombras
La API de sombras da cuatro propiedades:
shadowColor
: Establece el color de sombra deseado. Los valores permitidos son los mismos que se permiten en los valores de color CSS.shadowBlur
: Establece la cantidad de desenfoque de la sombra, en píxeles. Cuanto menor sea el valor de desenfoque, más nítida es la sombra. Da un efecto muy similar al desenfoque gaussiano de Photoshop.shadowOffsetX
yshadowOffsetY
: Especifica el desplazamiento X e Y de la sombra, de nuevo en píxeles.
El uso es muy sencillo, como demuestra el siguiente ejemplo de código de sombra canvas:
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = 'rgba(255, 0, 0, 0.5)';
context.fillStyle = '#00f';
context.fillRect(20, 20, 150, 100);
Genera lo mostrado en la figura 6.
Figura 6: Ejemplo de sombra canvas. Rectángulo azul con sombra roja.
Degradados
A las propiedades fillStyle
y strokeStyle
también se les puede asignar objetos CanvasGradient
, en lugar de cadenas de color CSS: estos permiten utilizar gradientes de color para líneas y rellenos en lugar de colores sólidos.
Para crear objetos CanvasGradient
puedes utilizar dos métodos: createLinearGradient
y createRadialGradient
. El primero crea un gradiente lineal —líneas de color todas en una dirección— mientras que el segundo crea un gradiente radial —círculos de color emanando desde un solo punto.
Una vez tienes el objeto gradiente puedes agregar saltos de color a lo largo del gradiente usando el método addColorStop
del objeto.
El código siguiente muestra cómo utilizar gradientes:
// Hay que dar las coordenadas (x,y) de origen y destino
// para el gradiente (dónde empieza y dónde acaba).
var gradient1 = context.createLinearGradient(sx, sy, dx, dy);
// Ahora se puede añadir colores al gradiente.
// El primer argumento da la posición del color en el gradiente. El
// rango de valores aceptados va de 0 (inicio del gradiente) a 1 (final del gradiente).
// El segundo argumento da el color deseado, con el formato de color de CSS.
gradient1.addColorStop(0, '#f00'); // rojo
gradient1.addColorStop(0.5, '#ff0'); // amarillo
gradient1.addColorStop(1, '#00f'); // azul
// Para el gradiente radial también hay que dar
// fuente y el radio del círculo de destino.
// Las coordenadas (x,y) definen el punto central del
// círculo (inicio y destino).
var gradient2 = context.createRadialGradient(sx, sy, sr, dx, dy, dr);
// Añadir colores a un gradiente radial es igual que
// añadir colores a gradientes lineales.
También he preparado un ejemplo más avanzado, que hace uso de un degradado lineal, sombras y texto. El ejemplo produce el resultado que se ve en la Figura 7.
Figura 7: Ejemplo de salida usando un gradiente lineal.
Demos en línea de canvas
Si quieres ver lo que han hecho otros con Canvas, puedes echar una ojeada a los siguientes proyectos:
- Widgets Opera:
- Proyectos y demos en línea
Resumen
Canvas es una de las características más interesantes de HTML5, y puede usarse en la mayoría de navegadores web modernos. Ofrece todo lo necesario para crear juegos, mejoras en la interfaz de usuario y aún más cosas. La API de contexto 2D incluye una gran cantidad de funcionalidades además de las que se tratan en este artículo. Espero que hayas obtenido una buena base de canvas, y sed de saber más.