IE9 ha visto la luz en un momento en que las amplias capacidades de los equipos de cliente son el objetivo prioritario de todos los programadores. Los navegadores actuales como Chrome, Firefox e Internet Explorer 9 y 10 lo soportan ya. Pero ¿qué es exactamente el elemento canvas de HTML5? ¿Cómo podemos utilizarlo para crear aplicaciones web avanzadas y extraordinariamente ricas?
Si no estás familiarizado con HTML5, antes de empezar a leer este artículo te recomiendo que visites este sitio para aprender más.
¿Para qué sirve el elemento Canvas?
Oficialmente, un canvas es un lienzo de mapa de bits dependiente de la resolución de pantalla que se puede utilizar para representar gráficos, imágenes de juegos o cualquier otra información de este tipo sobre la marcha. En palabras de layman, el canvas es un nuevo elemento de HTML5 que nos permite dibujar gráficos con Javascript. Nos sirve para representar en la pantalla textos, imágenes, gráficos de todo tipo, rectángulos, líneas, gradientes y efectos de forma dinámica. El dibujo en el canvas se hace mediante el API Canvas 2D. Esta API contiene multitud de funciones con las que prácticamente podemos dibujar cualquier cosa sobre él. En este momento el canvas soporta una superficie en 2D, no en 3D. Así que ¿qué aspecto nos ofrece el canvas? Pues nada especial: míralo tú mismo.
<canvas id="myCanvas" style="width:300px; height:300px"></canvas>
Este código nos muestra un canvas en la ventana del navegador, pero como está vacío, no vemos nada. Vamos a añadirle un borde, para verlo más claramente. La imagen siguiente nos muestra el canvas con un borde negro.
Por el momento, nada destacable, pero ¡ya verás cómo cambia todo si sigues leyendo! Una página web puede llevar múltiples elementos canvas. Si individualizamos cada uno con su id, tenemos la posibilidad de manipularlos de forma independiente mediante Javascript. Para dibujar dentro de un canvas necesitamos referenciar el contexto del canvas. El contexto nos permite acceder a las propiedades y métodos del API 2D con los que vamos a dibujar y manipular imágenes. Veremos el contexto con más detalle en un momento.
El elemento canvas tiene coordenadas X e Y, donde X es la distancia en el eje horizontal y la Y es la vertical. En la siguiente imagen vemos estas coordenadas del canvas.
Aclaración sobre la relación entre Canvas y SVG
Resulta especialmente importante conocer las diferencias entre los elementos canvas y SVG. SVG es un formato de gráficos vectoriales (de ahí su nombre) basado en XML. Podemos añadirle estilos utilizando CSS y comportamientos dinámicos desde el DOM, porque los elementos SVG forman parte del DOM. El canvas nos permite dibujar gráficos y formas utilizando Javascript. También podemos aplicarle estilos y añadirle dinamismo. Estas son algunas razones por las que conviene utilizar canvas frente a SVG. El canvas es más rápido a la hora de representar gráficos complejos. Podemos guardar imágenes fuera del canvas, cosa que no podemos hacer con SVG. Todo dentro del canvas son pixels.
- Pero por otro lado, SVG también tiene una serie de ventajas sobre el canvas:
- Es independiente de la resolución de pantalla, pudiendo escalar para adaptarse a distintas resoluciones y formatos.
- Se basa en XML, así que el manejo de sus diferentes elementos y atributos es realmente fácil.
- Es mejor solución para animaciones complejas.
Entonces ¿Cuándo tenemos que utilizar uno u otro? Bueno, si tú quieres que el aspecto de tu sitio web sea independiente de la resolución de pantalla, altamente interactivo y prefieres editar imágenes vectoriales, te conviene SVG. Si, por el contrario, estás desarrollando un juego y quieres que los gráficos se muestren en pantalla a gran velocidad, o no quieres trabajar con XML, lo tuyo es el canvas. Idealmente, ambos elementos pueden funcionar juntos de forma complementaria a la perfección.
Si te interesa saber más sobre la elección de SVG o canvas, lee este blog.
El Canvas y la aceleración por hardware
El uso del canvas es la mejor forma de conocer cómo funciona la aceleración de gráficos por hardware en la web. En versiones anteriores de los navegadores, la restitución de gráficos como ocurre con la mayoría de tareas que exigen un gran esfuerzo de computación- se maneja desde el procesador del sistema, la CPU. Los navegadores actuales han introducido una gran innovación en este terreno, ya que ahora entregan las tareas de restitución gráfica más exigentes a la GPU, la unidad de procesamiento de gráficos, para representar en pantalla gráficos y textos de las páginas web utilizando Direct2D y DirectWrite. La asignación de estas tareas a los núcleos de la GPU no solamente acelera el procesamiento de gráficos, sino que descarga a la propia CPU de modo que entre ambos subsistemas se reparten el trabajo de forma mucho más eficiente.
También se suele acusar a Javascript de ser un agobio para los procesadores. Un buen ejemplo de la evolución que se ha producido es el nuevo motor de Javascript de IE9, llamado Chakra, que aprovecha la potencia de las CPUs multicore para compilar en segundo plano el código Javascript y utilizar luego el código máquina para acelerar el procesamiento de las páginas.
En ambos casos el resultado final es un rendimiento muy superior.
Comento todo esto aquí porque si unimos un procesamiento de gráficos acelerado por hardware con un procesamiento de Javascript acelerado mediante compilación, tenemos un entorno ideal para aprovechar al máximo la potencia del canvas de HTML5 como base para creaciones gráficas avanzadas.
Cuando enviamos comandos de dibujo al canvas, los navegadores las entregan directamente al hardware de gráficos sin necesidad de desarrollar nada más por nuestra parte. La aceleración por hardware funciona a una velocidad increíble que nos permite incorporar animaciones en tiempo real y gráficos interactivos sin que la experiencia de usuario en su conjunto se vea afectada o ralentizada. Puedes probar el resultado en muchos navegadores distintos y valorar su nivel de soporte para la aceleración por hardware en este sitio.
Esto supone una auténtica liberación para el desarrollador creativo, ya que sabe que las experiencias visuales que añade al código se van a traducir y representar tal y como él pretende.
Todas las siguientes opciones de trabajo con el canvas nos ofrecen una experiencia mejorada, más intensa y gratificante cuando se ven en un navegador con aceleración por hardware.
EL API 2D de Canvas
El API 2D del canvas es un objeto que nos permite dibujar y manipular imágenes y gráficos dentro de un elemento canvas. Para referenciar el contexto del canvas, tenemos que hacer una llamada a getContext, que es un método del elemento canvas. Tiene un parámetro, que actualmente es 2d. Este es un ejemplo de código para referenciar el contexto:
var myCanvas = document.getElementById("myCanvas");
var context = myCanvas.getContext("2d");
var context = myCanvas.getContext("2d");
Cada canvas tiene su propio contexto, de modo que si tu página tiene varios elementos canvas, tienes que referenciar sus contextos de forma individual para poder trabajar con cada uno de ellos.
Aparte de getContext, tenemos multitud de otras funciones a nuestra disposición en el API 2D. Algunas de las más destacadas os las enumero a continuación:
Funciones de transformación
- scale - permite cambiar la escala del contexto actual.
- rotate permite rotar las coordenadas x e y del contexto actual.
Funciones de estado
- save - nos permite guardar el estado actual del contexto.
- restore permite recuperar el estado del contexto desde un estado guardado previamente.
Funciones de texto
- font declara u obtiene el nombre de la fuente en el contexto actual.
- fillText dibuja texto sólido en el canvas.
- measureText mide la anchura del texto especificado.
Como puedes imaginarte, existen más métodos dentro del API 2D. Puedes visitar esta página si deseas ver todos los métodos disponibles, incluso los que no veremos en este artículo.
Un canvas queda bastante soso si no dibujamos en él, así que vamos a ver qué cosas podemos dibujar ahí cuando ya tenemos el contexto a mano.
Formas y colores
Hay un grupo completo de propiedades y métodos orientados al dibujo de formas. Vamos a empezar con los rectángulos. Estos son los métodos que tenemos para dibujar rectángulos:
- fillRect(x, y, w, h) dibuja el rectángulo en cuestión dentro del canvas utilizando el estilo de relleno actual.
- strokeRect(x, y, w, h) dibuja el recuadro que delimita el rectángulo dentro del canvas, utilizando el estilo actual de línea.
- clearRect(x, y, w, h) borra los pixels del canvas que quedan dentro del área del rectángulo, dejándolos en negro transparente (valores RGB y Alpha a cero).
Para dibujar un rectángulo, lo más sencillo es utilizar fillRect. Este método dibuja un rectángulo dentro del canvas utilizando el patrón de relleno actual fillStyle. Aquí vemos cómo se genera un rectángulo negro:
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.fillRect(5, 5, 145, 145);
Este código dibujará un rectángulo negro a una distancia de 5 pixels del borde superior, 5 a contar desde el lado izquierdo y anchura y altura de 145 pixels (en realidad es un cuadrado). En la imagen siguiente vemos como queda en pantalla:
Si no se indica un color, el color por defecto siempre será negro. Para dibujar otro rectángulo encima, llamamos otra vez a la función fillRect con otros parámetros. El valor de fillStyle puede ser cualquier color CSS, y podemos también utilizar la nueva opacity de CSS3. El código siguiente dibuja tres rectángulos en el canvas y cambia el color del último de ellos, de modo que aparece casi transparente:
context.fillRect(5, 5, 145, 145);
context.fillStyle = "rgb(0, 162, 232)";
context.fillRect(40, 55, 145, 145);
context.fillStyle = "rgba(255, 0, 0, 0.2)";
context.fillRect(75, 105, 145, 145);
context.fillStyle = "rgb(0, 162, 232)";
context.fillRect(40, 55, 145, 145);
context.fillStyle = "rgba(255, 0, 0, 0.2)";
context.fillRect(75, 105, 145, 145);
El resultado:
Dibujar círculos también es muy fácil. Para ello lo más sencillo es usar el método arc. Este método dibuja un círculo en el canvas utilizando el valor fillStyle actual. La definición de este método es así:
- arc(x, y, radius, startAngle, endAngle, anticlockwise) dibuja puntos sobre un trazado, formando el arco descrito por la circunferencia del círculo definido por los argumentos (coordenadas x e y del centro y el radio, en pixels), empezando desde el ángulo indicado por startAngle y finalizando en el punto indicado en endAngle, siguiendo la dirección indicada en el último argumento (sentido horario o anti horario).
Aquí vemos cómo se genera un círculo negro.
context.beginPath();
context.fillStyle = "rgb(0, 0, 0)";
context.arc(123, 93, 70, 0, 2 * Math.PI, true);
context.fill();
context.fillStyle = "rgb(0, 0, 0)";
context.arc(123, 93, 70, 0, 2 * Math.PI, true);
context.fill();
Puesto que arc va añadiendo puntos al trazado, tenemos antes que llamar al método beginPath y después hacemos la llamada a fill. Este método rellena los trazados con el estilo actual definido en fillStyle. Aquí vemos el resultado:
Para dibujar la circunferencia e vez del círculo, utilizamos strokeStyle en vez de fillStyle. Después hacemos una llamada a stroke en lugar de fill.
context.beginPath();
context.strokeStyle = "rgb(0, 0, 0)";
context.arc(123, 93, 70, 0, 2 * Math.PI, true);
context.stroke();
context.strokeStyle = "rgb(0, 0, 0)";
context.arc(123, 93, 70, 0, 2 * Math.PI, true);
context.stroke();
Los círculos no necesariamente tienen que tener 360 grados. Para alterar la forma, podemos variar el valor inicial y final del ángulo:
context.beginPath();
context.strokeStyle = "rgb(0, 0, 0)";
context.arc(123, 93, 70, 0, 1.5 * Math.PI, true);
context.stroke();
context.strokeStyle = "rgb(0, 0, 0)";
context.arc(123, 93, 70, 0, 1.5 * Math.PI, true);
context.stroke();
Y el resultado es un arco de 270 grados:
Vamos ahora a algo más avanzado: vamos a ver cómo se dibujan curvas de Bézier. Esta cosa tan difícil se hace relativamente sencilla utilizando la función bezierCurveTo, que va añadiendo puntos al trazado actual utilizando los puntos de control que definen la curva de Bèzier. Los parámetros de la función bezierCurveTo son estos:
- bezierCurveTo (cp1x, cp1y, cp2x, cp2y, x, y) añade el punto dado al trazado actual, conectado con el anterior mediante una curva Bèzier cúbica con los puntos de control especificados.
Una curva de Bèzier viene definida por tres puntos. Los dos primeros controlan su cálculo y el tercero es el punto de finalización de la curva. La forma de crear una curva de Bèzier sencilla sería así:
context.lineWidth = 20;
context.beginPath();
context.moveTo(5, 50);
context.bezierCurveTo(30, 30, 130, 530, 200, 100);
context.stroke();
context.beginPath();
context.moveTo(5, 50);
context.bezierCurveTo(30, 30, 130, 530, 200, 100);
context.stroke();
En la imagen siguiente vemos lo que se habría dibujado en el canvas.
Las curvas de Bèzier son una herramienta tremendamente potente para el dibujo. El siguiente es un ejemplo avanzado de dibujo de una cara sonriente en el canvas:
// Cara
context.beginPath();
context.lineWidth = 10;
context.strokeStyle = "rgb(0, 0, 0)";
context.arc(200, 233, 150, 0, 2 * Math.PI, true);
context.stroke();
// Color de la cara
context.beginPath();
context.fillStyle = "rgba(80, 100, 80, 0.4)";
context.arc(200, 233, 150, 0, 2 * Math.PI, true);
context.fill();
// ojo derecho
context.lineWidth = 20;
context.beginPath();
context.moveTo(230, 130);
context.bezierCurveTo(230, 130, 230, 130, 240, 200);
context.stroke();
// ojo izquierdo
context.beginPath();
context.moveTo(170, 130);
context.bezierCurveTo(170, 130, 170, 130, 160, 200);
context.stroke();
// labio superior
context.beginPath();
context.moveTo(100, 230);
context.bezierCurveTo(100, 230, 200, 380, 300, 230);
context.stroke();
// labio inferior
context.beginPath();
context.moveTo(100, 235);
context.bezierCurveTo(105, 270, 200, 480, 300, 232);
context.stroke();
context.beginPath();
context.lineWidth = 10;
context.strokeStyle = "rgb(0, 0, 0)";
context.arc(200, 233, 150, 0, 2 * Math.PI, true);
context.stroke();
// Color de la cara
context.beginPath();
context.fillStyle = "rgba(80, 100, 80, 0.4)";
context.arc(200, 233, 150, 0, 2 * Math.PI, true);
context.fill();
// ojo derecho
context.lineWidth = 20;
context.beginPath();
context.moveTo(230, 130);
context.bezierCurveTo(230, 130, 230, 130, 240, 200);
context.stroke();
// ojo izquierdo
context.beginPath();
context.moveTo(170, 130);
context.bezierCurveTo(170, 130, 170, 130, 160, 200);
context.stroke();
// labio superior
context.beginPath();
context.moveTo(100, 230);
context.bezierCurveTo(100, 230, 200, 380, 300, 230);
context.stroke();
// labio inferior
context.beginPath();
context.moveTo(100, 235);
context.bezierCurveTo(105, 270, 200, 480, 300, 232);
context.stroke();
Y la cara que obtenemos con este código sería así:
Para complementar las formas posibles en un canvas, podemos mezclar también los colores con colores sólidos, bordes, gradientes y patrones. En los ejemplos anteriores he utilizado fillStyle. Esta función sirve para rellenar el fondo del contexto con un color sólido. El número de colores es extraordinariamente elevado. En el ejemplo siguiente vamos a generar un valor progresivo para el color de fondo del canvas, y producir un efecto de arco iris:
var a = 1;
for (j = 0; j < 100; j++) {
var r = 255, g = 0, b = 0;
for (i = 0; i < 150; i++) {
// Amarillo
if (i < 25) g += 10.2;
// Verde
else if (i >= 25 && i < 50) r -= 10.2;
// Azul
else if (i >= 50 && i < 75) {
g -= 10.2;
b += 10.2;
}
// Morado
else if (i >= 75 && i < 100) r += 10.2;
// Rojo
else b -= 10.2;
context.fillStyle = "rgba(" + Math.floor(r) + "," +
Math.floor(g) + "," + Math.floor(b) + "," + a + ")";
context.fillRect(3 * i, 5 * j, 3, 5);
}
a -= 0.01;
}
for (j = 0; j < 100; j++) {
var r = 255, g = 0, b = 0;
for (i = 0; i < 150; i++) {
// Amarillo
if (i < 25) g += 10.2;
// Verde
else if (i >= 25 && i < 50) r -= 10.2;
// Azul
else if (i >= 50 && i < 75) {
g -= 10.2;
b += 10.2;
}
// Morado
else if (i >= 75 && i < 100) r += 10.2;
// Rojo
else b -= 10.2;
context.fillStyle = "rgba(" + Math.floor(r) + "," +
Math.floor(g) + "," + Math.floor(b) + "," + a + ")";
context.fillRect(3 * i, 5 * j, 3, 5);
}
a -= 0.01;
}
El efecto resultante es el que vemos aquí:
Si no quieres utilizar colores sólidos, puedes emplear strokeStyle y strokeRect para dibujar el borde de un rectángulo. Otra característica del canvas es que nos permite crear gradientes. Las funciones que hacen esto son las siguientes:
- addColorStop(offset, color) añade un punto de detención de color con el color indicado a un gradiente a la distancia especificada. El valor 0.0 (ojo al punto decimal), es el desplazamiento hacia un extremo del gradiente. 1.0 es el desplazamiento hacia el extremo contrario.
- createLinearGradient(x0, y0, x1, y1) devuelve un objeto CanvasGradient que representa un gradiente lineal que se desarrolla (se pinta) a lo largo de la línea descrita por las coordenadas.
- createRadialGradient(x0, y0, r0, x1, y1, r1) devuelve un objeto CanvasGradient que representa un gradient circular que se pinta sobre una superficie cónica delimitada por los dos círculos descritos por sus coordenadas en los argumentos de la función.
Podemos crear un gradiente lineal con una llamada a createLinearGradient. Para añadir color al gradiente utilizamos addColorStop. Con esta llamada insertamos el color especificado en las coordenadas x e y. El siguiente ejemplo es una demostración de un gradiente lineal sencillo:
var gradient = context.createLinearGradient(0, 0,0, 145);
gradient.addColorStop(0, "#00ABEB");
gradient.addColorStop(0.5, "yellow");
gradient.addColorStop(0.8, "green");
gradient.addColorStop(1, "white");
context.fillStyle = gradient;
context.fillRect(5, 5, 145, 145);
gradient.addColorStop(0, "#00ABEB");
gradient.addColorStop(0.5, "yellow");
gradient.addColorStop(0.8, "green");
gradient.addColorStop(1, "white");
context.fillStyle = gradient;
context.fillRect(5, 5, 145, 145);
El resultado es este que vemos aquí:
Hay muchos más gradientes de los que no he hablado. Si quieres conocer más sobre todo este mundo, puedes visitar el sitio web del W3C.
Líneas, textos y sombras
Cuando vayas a dibujar líneas tienes que pensar en trazados (o paths). Cada canvas tiene un trazado. La definición de un trazo es como dibujar una línea: cualquier línea es posible. E igual que al escribir, necesitamos primero definir un trazado (dirección y longitud) y después llenarlo con algo. Estas son algunas de las propiedades y funciones que tienen que ver con el dibujo de líneas:
- lineWidth [ = valor ] devuelve la anchura actual de la línea. Se puede modificar, para variar el ancho de la propia línea.
- lineCap [ = valor ] devuelve el estilo actual de terminación del trazo. Se puede modificar y los estilos posibles son redondeado (round), cuadrado (square) y sin terminación (butt).
- lineJoin [ = valor ] devuelve el valor del estilo de unión. Se puede modificar y los posibles valores son bevel (sin unión), round (redondeado) y miter (en ángulo).
Para dibujar líneas se utilizan las funciones moveTo y lineTo. Ambas aceptan los parámetros X e Y que les indican el punto exacto donde empieza y termina de dibujarse la línea. También podemos indicar la anchura de la línea con la función lineWidth. Una vez definida la línea, tenemos que hacer una llamada al método stroke para que se muestre en pantalla.
El siguiente ejemplo muestra cómo dibujar una serie de líneas con una anchura decreciente.
for (i = 15; i > 0; i--) {
context.strokeStyle = "blue";
context.lineWidth = i;
context.beginPath();
context.moveTo(55, 15 + (15 - i) * 24);
context.lineTo(335, 15 + (15 - i) * 24);
context.stroke();
}
context.strokeStyle = "blue";
context.lineWidth = i;
context.beginPath();
context.moveTo(55, 15 + (15 - i) * 24);
context.lineTo(335, 15 + (15 - i) * 24);
context.stroke();
}
Este es el resultado:
Para decorarlas un poco, podemos alterar su estilo de terminación (el line cap), modificando la propiedad lineCap . Por ejemplo, podemos darle a las líneas una terminación redondeada poniendo el valor de lineCap como round:
context.lineCap = "round";
Y con esto, las líneas del ejemplo anterior quedarían así:.
Escribir textos en un canvas también es muy sencillo. A diferencia de lo que ocurre con el texto normal en una página web, no se basa en un modelo de cuadro o caja, lo que quiere decir que no podemos utilizar los estilos CSS para este tipo de textos. Pero tenemos otras opciones. Algunas de las propiedades y métodos relacionados con la escritura dentro de un canvas son:.
- font [ = valor ] devuelve el nombre de la fuente de texto en uso. Se puede modificar. La sintaxis es la misma que se emplea en la propiedad font de CSS.
- textAlign [ = valor ] devuelve el estilo de alineamiento del texto en uso. Se puede modificar y los valores posibles son start, end, left, right y center.
- textBaseline [ = valor ] devuelve el valor en uso de alineamiento vertical del texto. Se puede modificar.
- fillText(texto, x, y [, maxAncho ] ) Rellena el texto en una posición dada
- strokeText(texto, x, y [, maxAncho ] ) dibuja los caracteres en la posición indicada
Para dibujar un texto plano simple podemos utilizar fillText. Con la propiedad font establecemos el tamaño y tipo de letra:
context.font = '24px "Tahoma"';
context.fillText("Hello World!", 0, 0);
context.fillText("Hello World!", 0, 0);
Y el resultado sería éste :
Si lo que queremos es un texto ligeramente transparente, modificamos el valor de fillStyle.
context.fillStyle = "rgba(0, 0, 0, 0.2)";
Con lo que obtenemos:
También resulta muy fácil añadir otros efectos, como sombras por ejemplo. El siguiente código utiliza los objetos de dibujo shadow para crear sombras:
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 5;
context.shadowColor = "rgba(0, 0, 0, 1)";
context.shadowOffsetY = 5;
context.shadowBlur = 5;
context.shadowColor = "rgba(0, 0, 0, 1)";
La sombra puede personalizarse indicando cualquier ángulo o color que queramos. Aquí solo estoy demostrando una pequeña parte de todo cuanto podemos hacer con el texto en un canvas. Si quieres aprender más sobre este tema, puedes leer la sección de Texto en el sitio del W3C.
Imágenes y vídeo
Bueno, con el canvas podemos también utilizar imágenes y vídeos. La pregunta evidente es ¿por qué no utilizar un elemento <img> normal? La ventaja principal del canvas en este caso es que la imagen puede formar parte de una composición gráfica mucho más grande y compleja gracias a toda fantasía que nos permite el elemento canvas. El siguiente ejemplo de código toma la imagen de una ovejita y la pinta dentro del canvas:
var image = document.getElementById("mySheep");
context.drawImage(image, 10, 10, 128, 128);
<img id="mySheep" src="sheep.png" style="display:none;" />
context.drawImage(image, 10, 10, 128, 128);
<img id="mySheep" src="sheep.png" style="display:none;" />
Y la simpática ovejita ya está dentro del canvas:.
Ahora podemos rotar la imagen, invertir su orientación, darle transparencia o simplemente hacerla girar a voluntad.
Los vídeos se manejan de manera similar. Primero necesitamos utilizar el elemento <video> de HTML5. Como puedes suponer, este elemento tiene montones de funcionalidades asociadas, así que antes de empezar te conviene leer algo sobre este tema aquí. El elemento video por sí mismo no es tan interesante. Contiene un atributo para mostrar los controles, que indica al navegador si debe mostrar o no una barra normal de control de la reproducción (parada, avance, control de volumen, etc.), y un valor de bucle que le dice si debe reproducirse de forma indefinida. Dentro del elemento video podemos especificar tres hijos, todos apuntando al mismo contenido de vídeo, pero en distintos formatos. Nada nuevo por aquí, pero cuando juntamos el elemento de vídeo con el canvas, el resultado sí que puede ser realmente sorprendente.
Primero vamos a añadir los elementos de canvas y vídeo en HTML:
<canvas id="myCanvas"></canvas>
<video id="myVideo" controls loop>
<source src="video.webm" type="video/webm">
<source src="video.ogg" type="video/ogg">
<source src="video.mp4" type="video/mp4">
</video>
<video id="myVideo" controls loop>
<source src="video.webm" type="video/webm">
<source src="video.ogg" type="video/ogg">
<source src="video.mp4" type="video/mp4">
</video>
En este punto no ocurre nada especial. El vídeo se reproduce dentro de la etiqueta de vídeo, pero ahora voy a dibujar el vídeo dentro del canvas y centrarlo en la superficie del lienzo. El resultado ahora sí que es especial. Aquí va el código:
var canvas = function () {
return {
draw: function () {
var video = document.getElementById("myVideo");
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var width = Math.floor(canvas.clientWidth / 100);
var height = Math.floor(canvas.clientHeight / 100);
canvas.width = width;
canvas.height = height;
video.play();
context.drawImage(video, 0, 0, width, height);
},
init: function () {
setInterval(canvas.draw, 16);
}
}
} ();
return {
draw: function () {
var video = document.getElementById("myVideo");
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var width = Math.floor(canvas.clientWidth / 100);
var height = Math.floor(canvas.clientHeight / 100);
canvas.width = width;
canvas.height = height;
video.play();
context.drawImage(video, 0, 0, width, height);
},
init: function () {
setInterval(canvas.draw, 16);
}
}
} ();
El truco que hace funcionar esto es la llamada recursiva para redibujar el vídeo dentro del canvas, porque de otra forma solo se pintaría una vez y podría verse mal y descolocado. Por eso hago una llamada a setInterval al cargar la página. El vídeo se puede ver en acción aquí.
Transformaciones y animaciones
En un elemento canvas podemos añadir animaciones y transformaciones. Algunas de las propiedades y métodos que tienen que ver con las transformaciones son:
- scale(x, y) cambia la matriz de transformación para aplicar un cambio de escala con las características indicadas.
- rotate(ángulo) cambia la matriz de transformación para aplicar una rotación con las características indicadas.
- translate(x, y) cambia la matriz de transformación para aplicar una traslación con las características indicadas.
- transform(m11, m12, m21, m22, dx, dy) cambia la matriz de transformación para aplicar la nueva matriz indicada en los argumentos.
- setTransform(m11, m12, m21, m22, dx, dy) cambia la matriz de transformación a la matriz indicada por los argumentos.
Transformaciones y animaciones pueden ir por separado, pero cuando se combinan como veremos ahora- podemos obtener unos resultados visuales realmente impactantes. Empecemos con la rotación. Para girar el contexto, le pasamos como argumento el valor del ángulo y la función hace girar todo el canvas. En el ejemplo siguiente se dibuja un rectángulo cada 250 milisegundos y a cada rectángulo se le aplica una rotación, de modo que el efecto simula una especie de abanico. El valor de color se calcula aleatoriamente también, para obtener un resultado más espectacular:
var can = function () {
var canvas;
var context;
return {
draw: function () {
var r = Math.floor(Math.random() * 255) + 70;
var g = Math.floor(Math.random() * 255) + 70;
var b = Math.floor(Math.random() * 255) + 70;
var s = 'rgba(' + r + ',' + g + ',' + b + ', 0.5)';
context.rotate(0.2 * Math.PI);
context.fillStyle = s;
context.fillRect(10, 0, 150, 50);
},
init: function () {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
context.translate(200, 250);
setInterval(can.draw, 250);
}
}
} ();
window.onload = can.init;
var canvas;
var context;
return {
draw: function () {
var r = Math.floor(Math.random() * 255) + 70;
var g = Math.floor(Math.random() * 255) + 70;
var b = Math.floor(Math.random() * 255) + 70;
var s = 'rgba(' + r + ',' + g + ',' + b + ', 0.5)';
context.rotate(0.2 * Math.PI);
context.fillStyle = s;
context.fillRect(10, 0, 150, 50);
},
init: function () {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
context.translate(200, 250);
setInterval(can.draw, 250);
}
}
} ();
window.onload = can.init;
La imagen resultante quedaría así:
La imagen estática no hace un gran favor a esta demostración, así que si quieres ver cómo funciona realmente, puedes visitar esta página. El cambio de escala del canvas es también muy simple. Voy a utilizar la misma demo, excepto en la parte de la rotación y vamos a cambiar el tamaño de cada uno de los rectángulos a intervalos de un segundo. Este sería el código:
var can = function () {
var canvas;
var context;
var x = 0;
var y = 0;
function currectX() {
return x = x + 1;
}
function currectY() {
return y = y + 1;
}
return {
draw: function () {
var r = Math.floor(Math.random() * 255) + 70;
var g = Math.floor(Math.random() * 255) + 70;
var b = Math.floor(Math.random() * 255) + 70;
var s = 'rgba(' + r + ',' + g + ',' + b + ', 0.5)';
context.fillStyle = s;
context.scale(1.2,1.2);
context.fillRect(currectX(), currectY(), 5, 5);
},
init: function () {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
context.translate(0, 0);
setInterval(can.draw, 1000);
}
}
} ();
window.onload = can.init;
var canvas;
var context;
var x = 0;
var y = 0;
function currectX() {
return x = x + 1;
}
function currectY() {
return y = y + 1;
}
return {
draw: function () {
var r = Math.floor(Math.random() * 255) + 70;
var g = Math.floor(Math.random() * 255) + 70;
var b = Math.floor(Math.random() * 255) + 70;
var s = 'rgba(' + r + ',' + g + ',' + b + ', 0.5)';
context.fillStyle = s;
context.scale(1.2,1.2);
context.fillRect(currectX(), currectY(), 5, 5);
},
init: function () {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
context.translate(0, 0);
setInterval(can.draw, 1000);
}
}
} ();
window.onload = can.init;
El resultado final sería este:
De nuevo, esta imagen estática no nos muestra la animación, que puedes ver aquí. Como puedes ver, estamos viendo solo una mínima parte de todas las posibilidades de transformación y animación. Si te gustan las animaciones más complicadas, échale un vistazo a Brilliant Lines, una demo creada por el grupo del W3C.
El control del ratón
Como seguramente te habrás dado cuenta, también es posible manejar el ratón. Igual que con cualquier otro elemento de la página web, puedes saber las coordenadas X e Y del ratón solicitando los valores de las propiedades pageX y pageY del elemento. Para controlar la posición del ratón sobre el canvas, un ejemplo de código sencillo sería este:
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
canvas.onmousemove = function (e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
var div = document.getElementById("coords");
div.innerHTML = "x: " + x + " y: " + y;
};
var context = canvas.getContext("2d");
canvas.onmousemove = function (e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
var div = document.getElementById("coords");
div.innerHTML = "x: " + x + " y: " + y;
};
En este código he asociado un evento para capturar el movimiento del cursor, de modo que cuando se mueve, sus coordenadas horizontal y vertical se muestran en la página. Sencillo y limpio. Para conseguir algo más avanzado, he intentado convertir al canvas en algo parecido a un trozo de papel o una pizarra, en donde se pueda dibujar. Bueno, el código para esto no es demasiado complicado, lo pongo aquí completo:
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.fillCircle = function (x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
context.clearTo = function (fillColor) {
context.fillStyle = fillColor;
context.fillRect(0, 0, canvas.width, canvas.height);
};
context.clearTo("#ddd");
canvas.onmousemove = function (e) {
if (!canvas.isDrawing) return;
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
var div = document.getElementById("coords");
div.innerHTML = "x: " + x + " y: " + y;
var radius = 10;
var fillColor = '#ff0000';
context.fillCircle(x, y, radius, fillColor);
};
canvas.onmousedown = function (e) {
canvas.isDrawing = true;
};
canvas.onmouseup = function (e) {
canvas.isDrawing = false;
};
var context = canvas.getContext("2d");
context.fillCircle = function (x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
context.clearTo = function (fillColor) {
context.fillStyle = fillColor;
context.fillRect(0, 0, canvas.width, canvas.height);
};
context.clearTo("#ddd");
canvas.onmousemove = function (e) {
if (!canvas.isDrawing) return;
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
var div = document.getElementById("coords");
div.innerHTML = "x: " + x + " y: " + y;
var radius = 10;
var fillColor = '#ff0000';
context.fillCircle(x, y, radius, fillColor);
};
canvas.onmousedown = function (e) {
canvas.isDrawing = true;
};
canvas.onmouseup = function (e) {
canvas.isDrawing = false;
};
Gracias al Javascript puedo ampliar fácilmente el elemento canvas y añadirle algunos manejadores de eventos preparados por mí mismo para controlar el movimiento del ratón. Una versión funcional de este código la puedes ver aquí. ¡Anda, pruébala: dibuja alguna cosa!.
Esta es la verdadera magia del elemento canvas: ciertamente es mucho lo que podemos hacer.
Este ha sido un recorrido general, espero que con él te hayas aficionado a trabajar con el elemento canvas, ahora que sabes hasta dónde puedes llegar con estos efectos extraordinarios que puedes lograr con IE9 ya mismo.
Enlace: enlace
No hay comentarios:
Publicar un comentario