En HTML5 se incorporó esta nueva etiqueta o elemento, que nos permite, mediante scripting (javascript u otros lenguajes de programación), la generación de gráficas en 2D dinámicamente, y mucho más.
Canvas (que en español significa lienzo), es precisamente eso un lienzo en blanco, donde vamos a poder dibujar o trazar figuras, líneas, gráficos, hacer animaciones y desarrollar incluso juegos completos.
Canvas no es nuevo, fue introducido inicialmente por Apple (2004), y más tarde estandarizado para su uso en HTML.
Vamos por pasos, usar canvas es tan sencillo como incluir en tu documento html
, las etiquetas de apertura y cierre, <canvas></canvas>
.
Ahora bien así declarado, no visualizarías nada en el navegador, habrías declarado un lienzo con unas dimensiones por defecto de 300px de ancho (width) y 150px de alto (height), pero sin ningún otro estilo ó contenido.
Entonces empecemos con un ejemplo práctico donde ya visualicemos algo
<body>
<canvas id="canvas"></canvas>
</body>
#canvas {
/* Tiene el width y el height
por defecto (300x150) */
border: 1px solid black;
}
document.addEventListener("DOMContentLoaded", function() {
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
/* A partir de aquí incluiriamos
todos los métodos de javascript */
});
Unos apuntes, sobre el código de arriba, hemos identificado nuestro canvas con un atributo id
, que utilizaremos para darle los estilos CSS
y para identificarle en el código javascript
, con el que realmente podremos conseguir hacer cosas increibles con canvas.
Solo adelantar sobre javascript, es que para inicializar el canvas, lo primero es declarar el contexto en el que vamos a trabajar, que puede ser el 2d, 3d ó mapa de bits, el contexto le indica al navegador como deberá de renderizar el canvas, nosotros utilizaremos el context(2d)
.
Con canvas podremos dibujar formas geométricas, líneas, texto , imágenes, es decir hacer composiciones todo lo complejas que imagines.
Va a ser muy importante que seas siempre consciente de las dimensiones del canvas, y los puntos x, y
donde situas las figuras, por lo que te invito a que observes el siguiente ejemplo.
<p>Mouse indica coordenadas del canvas</p>
<div id="container">
<canvas id="coordenadas" width="300" height="300"></canvas>
<div id="salida"></div>
</div>
p {
margin: 0;
font-size: 12px;
}
#container {
position: relative;
width: 300px;
height: 300px;
}
#salida {
position: absolute;
padding: 5px;
min-width: 100px;
font-size: 14px;
top: 0px;
left: 0px;
background-color: transparent;
border: medium;
}
document.addEventListener("DOMContentLoaded", function() {
let canvas = document.getElementById("coordenadas");
if(canvas && canvas.getContext) {
let ctx = canvas.getContext("2d");
if(ctx) {
let salida = document.getElementById("salida");
canvas.addEventListener("mousemove", function(evt) {
let mousePos = oMousePos(canvas, evt);
marcarCoords(salida, mousePos.x, mousePos.y)
}, false);
canvas.addEventListener("mouseout", function(evt) {
limpiarCoords();
}, false);
}
}
function marcarCoords(object, x, y) {
salida.innerHTML = ("x: "+x+", y: "+y);
salida.style.top = (y+10)+"px";
salida.style.left = (x+10)+"px";
salida.style.backgroundColor= "#fff";
salida.style.border="1px solid #d9d9d9";
canvas.style.cursor = "pointer";
canvas.style.backgroundColor = "rgba(0,0,0,.5)";
}
function limpiarCoords() {
salida.innerHTML = "";
salida.style.top = 0+"px";
salida.style.left = 0+"px";
salida.style.backgroundColor = "transparent";
salida.style.border = "none";
canvas.style.cursor = "default";
canvas.style.backgroundColor = "transparent";
}
function oMousePos(canvas, evt) {
let clientRect = canvas.getBoundingClientRect();
return { //objeto
x: Math.round(evt.clientX - clientRect.left),
y: Math.round(evt.clientY - clientRect.top)
}
}
});
Si has visto el ejemplo, habrás observado que el canvas tenia unas dimensiones de 300x300 y que hemos empleado los métodos mousemove
y mouseout
de javascript, para detectar la posición dentro del canvas, darle algunos estilos e imprimirla dinámicamente en el <div>
salida.
Bueno con estas bases vamos a ir viendo más ejemplos para rellenar nuestros canvas.
Las líneas son útiles para realizar figuras más complejas, conectando unas líneas con otras. En este caso usando moveTo()
especificamos los ejes x e y desde donde partir, para después especificar con lineTo()
dónde queremos acabar. Agregamos color con strokeStyle
y grosor con lineWidth
a la línea si queremos.
<body>
<canvas id="canvas"></canvas>
</body>
#canvas {
/* Tiene el width y el height
por defecto (300x150) */
border: 1px solid black;
}
document.addEventListener("DOMContentLoaded", function() {
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
context.moveTo(50, 50);
context.lineTo(200, 100);
context.stroke();
context.beginPath();
context.moveTo(100, 100);
context.lineTo(200, 50);
context.lineWidth = 5;
context.strokeStyle = 'green';
context.stroke();
});
Si quisiéramos continuar la línea, podríamos seguir dándole instrucciones de eje en eje. Si por el contrario queremos añadir más líneas pero que no estén conectadas con la anterior, aplicando beginPath
al comienzo de cada una podrás añadir cuantas quieras.
Como ocurre con las líneas, utilizar beginPath es imprescindible si queremos dibujar más de una forma en el mismo lienzo, pero si sólo vamos a crear una no es necesario incluirlo.
<body>
<canvas id="canvas"></canvas>
</body>
#canvas {
width: 600px;
height: 200px;
border: 1px solid black;
}
document.addEventListener("DOMContentLoaded", function() {
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
context.beginPath();
context.arc(50, 50, 40, 0, 2 * Math.PI);
context.stroke();
context.beginPath();
context.arc(150, 50, 40, 0, Math.PI);
context.lineWidth = 5;
context.strokeStyle = 'black';
context.stroke();
context.fillStyle = 'red';
context.fill();
context.beginPath();
context.fillStyle = 'blue';
context.fillRect(200, 20, 80, 80);
});
Con la función arc()
, dibujamos arcos, primero se definen las coordenadas x e y del centro, seguido del radio del circulo, el ángulo en el que iniciar el arco y el de finalización. De entrada, arc() no crea una forma cerrada como fillRect()
, ya que puede usarse para dibujar arcos abiertos (como en el ejemplo). para aplicarle un contorno usaremos stroke()
y para dar color al contorno strokeStyle
, para rellenar de color por último, tenemos fillStyle
, cerrando con el método fill()
.
Con las funciones createLinearGradient()
y createRadialGradient()
, podremos rellenar nuestros canvas de color o de patrones, os muestro un ejemplo.
<body>
<canvas id="canvas-linear"></canvas>
<canvas id="canvas-radial"></canvas>
</body>
#canvas-linear, #canvas-radial {
/* Estilos por defecto del canvas.
Fijaros que por defecto tienen un
display: inline; */
}
document.addEventListener("DOMContentLoaded", function() {
let canvasl = document.getElementById("canvas-linear");
let contextl = canvasl.getContext("2d");
// Crear gradient
let grdlin = contextl.createLinearGradient(0, 0, 200, 0);
grdlin.addColorStop(0, "red");
grdlin.addColorStop(1, "white");
// Rellenar el fill del canvas con el gradient
contextl.fillStyle = grdlin;
contextl.fillRect(0, 0, 200, 100);
let canvasr = document.getElementById("canvas-radial");
let contextr = canvasr.getContext("2d");
let grdrdl = contextr.createRadialGradient(75, 50, 5, 90, 60, 100);
grdrdl.addColorStop(0, "fuchsia");
grdrdl.addColorStop(1, "aqua");
contextr.fillStyle = grdrdl;
contextr.fillRect(0, 0, 200, 100);
});
Como habéis visto, asignas a una variable la creación del gradiente y después esa variable es el fillStyle
del canvas.
Para incrustar texto en canvas tenemos la propiedad font
, con sus funciones fillText()
, para rellenar el texto y strokeText()
, para darle al texto solo el contorno.
Bueno aquí te muestro un ejemplo personal, donde empleo más propiedades, es un texto reflejado.
<body>
<canvas id="canvas-text"></canvas>
<canvas id="canvas"></canvas>
</body>
#canvas-text {
background-color: black;
}
document.addEventListener("DOMContentLoaded", function() {
let canvasText = document.getElementById("canvas-text");
if (canvasText && canvasText.getContext) {
let ctext = canvasText.getContext("2d");
if (ctext) {
ctext.save()
ctext.shadowBlur=5;
ctext.shadowOffsetX=2;
ctext.shadowOffsetY=2;
ctext.shadowColor="#fff";
let text = "José WEBMASTER";
ctext.font = "20pt Comic Sans MS";
ctext.textAlign = "center";
ctext.fillStyle = "silver";
// dibuja el texto
ctext.fillText(text,canvasText.width / 2, (canvasText.height/2)-2);
// translada el contexto en el centro del canvas
ctext.translate(canvasText.width / 2, (canvasText.height/2)+2);
// voltea horizontalmente el contexto
// el segundo parámetro de scale es un número negativo
ctext.scale(1, -1);
// transparencia
ctext.globalAlpha = 0.3;
// dibuja de nuevo el texto
ctext.fillText(text,0,0);
ctext.restore()
}
}
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
context.font = "30px Arial";
context.fillText("Hello World", 10, 50);
context.font = "30px Impact";
context.strokeText("Hello World", 50, 100);
});
También podemos trabajar con imágenes dentro de canvas. Aquí abajo podeís ver un ejemplo de una composición de imágenes reflejadas, para lo que utilizo propiedades como transform
de CSS.
<canvas id="canvas-img"></canvas>
#canvas-img {
display: block;
}
let canvasImg = document.getElementById("canvas-img");
let cimg = canvasImg.getContext("2d");
let cw = cimg.width = 345, cx = cw / 2;
let ch = cimg.height = 516, cy = ch / 2;
let img = new Image();
img.src = "../../pagina_inicio/imagenes/html2.png";
img.onload = function() {
cimg.scale(0.3,0.3);
cimg.drawImage(this, 0, 0);
cimg.translate(0, 2*this.height);
cimg.scale(1,-1);
cimg.drawImage(this, 0, 0);
}
let img1 = new Image();
img1.src = "../../pagina_inicio/imagenes/css3.png";
img1.onload = function() {
cimg.drawImage(this, 200, 0);
cimg.translate(100, 2*this.height);
cimg.scale(1,-1);
cimg.drawImage(this, 100, 0);
}
let img2 = new Image();
img2.src = "../../pagina_inicio/imagenes/js2.png";
img2.onload = function() {
cimg.drawImage(this, 300, 0);
cimg.translate(100, 2*this.height);
cimg.scale(1,-1);
cimg.drawImage(this, 200, 0);
}
Los siguientes ejemplos son una muestra de la magía que se puede lograr con canvas y estan inspirados en este sitio.
<body>
<p>NO ME TOQUES</p>
<canvas id="canvas-emoji"></canvas>
<canvas id="canvas-reloj"></canvas>
</body>
#canvas-emoji {
width: 200px;
height: 150px;
}
#canvas-reloj {
width: 270px;
height: 70px;
transform: translate(0%, -100%);
}
// Emoji canvas
var canvas = document.getElementById('canvas-emoji');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 70;
var eyeRadius = 10;
var eyeXOffset = 25;
var eyeYOffset = 20;
// draw the yellow circle
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 5;
context.strokeStyle = 'black';
context.stroke();
// draw the eyes
context.beginPath();
var eyeX = centerX - eyeXOffset;
var eyeY = centerY - eyeXOffset;
context.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
var eyeX = centerX + eyeXOffset;
context.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
context.fillStyle = 'black';
context.fill();
// draw the mouth
context.beginPath();
context.arc(centerX, centerY, 50, 0, Math.PI, false);
context.stroke();
canvas.addEventListener("mousemove", function(evt) {
if(context) {
context.save();
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'orangered';
context.fill();
context.beginPath();
var eyeX = centerX - eyeXOffset;
var eyeY = centerY - eyeXOffset;
context.arc(eyeX, eyeY, eyeRadius, 0, -1 * Math.PI, false);
var eyeX = centerX + eyeXOffset;
context.arc(eyeX, eyeY, eyeRadius, 0, -1 * Math.PI, false);
context.fillStyle = 'black';
context.fill();
context.beginPath();
context.moveTo(120, 45);
context.lineTo(200, 45);
context.lineWidth = 5;
context.strokeStyle = 'black';
context.stroke();
context.beginPath();
context.arc(centerX, centerY * 1.4, 30, 180, -2 * Math.PI, false);
context.stroke();
}
}, false);
canvas.addEventListener("mouseout",function(evt){
if(context) {
context.restore();
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 5;
context.strokeStyle = 'black';
context.stroke();
context.beginPath();
var eyeX = centerX - eyeXOffset;
var eyeY = centerY - eyeXOffset;
context.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
var eyeX = centerX + eyeXOffset;
context.arc(eyeX, eyeY, eyeRadius, 0, 2 * Math.PI, false);
context.fillStyle = 'black';
context.fill();
context.beginPath();
context.arc(centerX, centerY, 50, 0, Math.PI, false);
context.stroke();
}
}, false);
// Reloj canvas
var canvasClock = document.getElementById("canvas-reloj");
var c = canvasClock.getContext("2d");
function drawClock() {
// clear the background
c.fillStyle = 'lightgray';
c.fillRect(0, 0, canvasClock.width, canvasClock.height);
// Get the current time
var now = new Date(),
h = now.getHours(),
m = now.getMinutes(),
s = now.getSeconds(),
ampm = (h < 12 ? 'AM' : 'PM');
// Make the hour between 0 and 12 (not 24)
h = (h % 12);
// Make values like '5' into '05'
h = addLeadingZeroWhenNecessary(h);
m = addLeadingZeroWhenNecessary(m);
s = addLeadingZeroWhenNecessary(s);
// Assemble the text
var clockText = h + ':' + m + ':' + s + ' ' + ampm,
x = 30,
y = 90;
// This green color was picked
// using http://jscolor.com/
c.fillStyle = '#00DB84';
// Draw the text
c.font = '30pt Arial';
c.strokeStyle = 'black';
c.fillText(clockText, x, y);
c.strokeText(clockText, x, y);
}
function addLeadingZeroWhenNecessary(s){
return (s < 10 ? '0' : '') + s;
}
// Draw the clock right away
drawClock();
// Then draw the clock every subsequent second
setInterval(drawClock, 1000);
Solo indicar que para el ejemplo del reloj, empleamos las funciones date()
y setInterval()
de javascript.
Bueno espero que os haya gustado, esta amplia introducción a canvas y sus infinitas posibilidades, haber aclarado dudas a quien las tuviera y os emplazo a visitar la página animaciones canvas
, donde puedes ver, más ejemplos de canvas con animaciónes.