En este post sobre programación web HTML y JavaScript te mostraré cómo hacer que el usuario pueda dibujar en un canvas usando el mouse, algo así como un paint muy básico.
De esta forma vamos a permitir que el usuario pueda dibujar y pintar sobre el canvas usando el ratón.
Más adelante podrías adaptar este código para cualquier otra cosa, incluso para crear un paint completo. Aquí solo te mostraré cómo permitir que el usuario dibuje en la web con un color y grosor determinado.
Explicación del algoritmo
Vamos a escuchar los siguientes eventos con JavaScript: mousedown, mousemove, mouseup, mouseout. Y en cada evento vamos a establecer banderas, dibujar puntos o líneas según sea el caso.
En el caso del mousedown
es cuando el usuario presiona el botón del ratón, y mouseup
es cuando suelta el botón. En cambio mouseout
es cuando el puntero sale del canvas, y mousemove
es cuando el puntero se mueve sobre el canvas.
Escuchar mouseout y mouseup
["mouseup", "mouseout"].forEach(nombreDeEvento => {
$canvas.addEventListener(nombreDeEvento, () => {
haComenzadoDibujo = false;
});
});
Estos eventos van a establecer la bandera que indica que el usuario está presionado el botón en false
.
Ya que si algo de esto pasa (soltar el mouse o salir del canvas) significa que el usuario ya no está manteniendo presionado el botón.
Evento mousedown
$canvas.addEventListener("mousedown", evento => {
// En este evento solo se ha iniciado el clic, así que dibujamos un punto
xAnterior = xActual;
yAnterior = yActual;
xActual = obtenerXReal(evento.clientX);
yActual = obtenerYReal(evento.clientY);
contexto.beginPath();
contexto.fillStyle = COLOR;
contexto.fillRect(xActual, yActual, GROSOR, GROSOR);
contexto.closePath();
// Y establecemos la bandera
haComenzadoDibujo = true;
});
Aquí vamos a establecer la bandera que indica que el usuario está presionando el botón en true
.
Además, vamos a dibujar un punto, ya que si el usuario da un clic y no mueve el puntero es porque solo quiso dibujar un punto.
mousemove: dibujar
El evento en donde realmente dibujamos es el evento mousemove
, pero solo dibujamos sobre el canvas si es que la bandera que indica que el usuario está presionado el botón está en true
.
$canvas.addEventListener("mousemove", (evento) => {
if (!haComenzadoDibujo) {
return;
}
// El mouse se está moviendo y el usuario está presionando el botón, así que dibujamos todo
xAnterior = xActual;
yAnterior = yActual;
xActual = obtenerXReal(evento.clientX);
yActual = obtenerYReal(evento.clientY);
contexto.beginPath();
contexto.moveTo(xAnterior, yAnterior);
contexto.lineTo(xActual, yActual);
contexto.strokeStyle = COLOR;
contexto.lineWidth = GROSOR;
contexto.stroke();
contexto.closePath();
});
En caso de que lo anteriormente mencionado se cumpla entonces dibujamos una línea conforme el usuario se va moviendo.
Obtener coordenadas reales
Por cierto, en las funciones anteriores pudiste notar que estoy invocando a obtenerXReal
y obtenerYReal
.
const obtenerXReal = (clientX) => clientX - $canvas.getBoundingClientRect().left;
const obtenerYReal = (clientY) => clientY - $canvas.getBoundingClientRect().top
Eso es para obtener la coordenada x
e y
reales, ya que el canvas no siempre comenzará en 0,0, el mismo puede estar ubicado en otro lugar.
Poniendo todo junto
Ya te mostré los eventos para que el usuario dibuje en nuestro mini prototipo de paint, ahora te mostraré el código completo en donde puedes cambiar el color del “pincel” y su grosor.
Comenzando con el código JavaScript tenemos lo siguiente. Primero obtengo una referencia al canvas con querySelector y después declaro mis variables de las coordenadas anteriores y actuales, ya que recuerda que debemos saber las coordenadas anteriores para saber desde dónde dibujar.
/*
____ _____ _ _ _
| _ \ | __ \ (_) | | |
| |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___
| _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \
| |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/
|____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___|
__/ | __/ |
|___/ |___/
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me /
------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
| IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/
const $canvas = document.querySelector("#canvas");
const contexto = $canvas.getContext("2d");
const COLOR = "black";
const GROSOR = 2;
let xAnterior = 0, yAnterior = 0, xActual = 0, yActual = 0;
const obtenerXReal = (clientX) => clientX - $canvas.getBoundingClientRect().left;
const obtenerYReal = (clientY) => clientY - $canvas.getBoundingClientRect().top;
let haComenzadoDibujo = false; // Bandera que indica si el usuario está presionando el botón del mouse sin soltarlo
$canvas.addEventListener("mousedown", evento => {
// En este evento solo se ha iniciado el clic, así que dibujamos un punto
xAnterior = xActual;
yAnterior = yActual;
xActual = obtenerXReal(evento.clientX);
yActual = obtenerYReal(evento.clientY);
contexto.beginPath();
contexto.fillStyle = COLOR;
contexto.fillRect(xActual, yActual, GROSOR, GROSOR);
contexto.closePath();
// Y establecemos la bandera
haComenzadoDibujo = true;
});
$canvas.addEventListener("mousemove", (evento) => {
if (!haComenzadoDibujo) {
return;
}
// El mouse se está moviendo y el usuario está presionando el botón, así que dibujamos todo
xAnterior = xActual;
yAnterior = yActual;
xActual = obtenerXReal(evento.clientX);
yActual = obtenerYReal(evento.clientY);
contexto.beginPath();
contexto.moveTo(xAnterior, yAnterior);
contexto.lineTo(xActual, yActual);
contexto.strokeStyle = COLOR;
contexto.lineWidth = GROSOR;
contexto.stroke();
contexto.closePath();
});
["mouseup", "mouseout"].forEach(nombreDeEvento => {
$canvas.addEventListener(nombreDeEvento, () => {
haComenzadoDibujo = false;
});
});
Y en el HTML tenemos un simple Canvas sobre el cual el usuario va a dibujar:
<!--
____ _____ _ _ _
| _ \ | __ \ (_) | | |
| |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___
| _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \
| |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/
|____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___|
__/ | __/ |
|___/ |___/
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me /
------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
| IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dibujar en canvas con JS - By Parzibyte</title>
<link rel="stylesheet" href="estilo.css">
</head>
<body>
<canvas id="canvas"></canvas>
<br>
<a href="https://parzibyte.me/blog">By Parzibyte</a>
<script src="script.js"></script>
</body>
</html>
Por cierto también está el CSS que permite colocar un borde al canvas. Esto es opcional, pero yo lo he dejado para que el usuario sepa los límites del lienzo de dibujo:
/*
____ _____ _ _ _
| _ \ | __ \ (_) | | |
| |_) |_ _ | |__) |_ _ _ __ _____| |__ _ _| |_ ___
| _ <| | | | | ___/ _` | '__|_ / | '_ \| | | | __/ _ \
| |_) | |_| | | | | (_| | | / /| | |_) | |_| | || __/
|____/ \__, | |_| \__,_|_| /___|_|_.__/ \__, |\__\___|
__/ | __/ |
|___/ |___/
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me /
------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
| IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/
#canvas{
border: 1px solid black;
}
Finalmente si quieres explorar el código en un repositorio te lo dejo por aquí. También te dejo un enlace a la demostración en línea.
Si te gusta este lenguaje te invito a ver más posts sobre JavaScript en mi blog.
Me ha sido muy útil este post, pero para versión móvil hace el scroll cuando intento firmar y solo pone un puntito.
He puesto los eventos touchstart, touchend y touchmove y así escribe, pero sigue detectando el scroll.
Tal vez hay que escuchar el evento scroll e invocar a preventDefault, o algo parecido.