tablas - ¿Cómo dibujo a mano sobre lienzo con JavaScript?
obtener datos de una tabla html javascript (6)
Pregunta
¿Cómo puedo dibujar libremente (usando mi mouse / dedos) en un elemento del lienzo como puedes hacerlo pintando con un lápiz?
Sobre esta pregunta
Hay muchas preguntas que quieren lograr el dibujo a mano alzada en lienzo:
- dibujar con el mouse con HTML5 Canvas
- KineticJS - Dibuja gratis con el mouse
- Dibujo libre sobre lienzo usando fabric.js
- Dibujando con JS
- Lienzo de pintura no funciona correctamente
- Posición del ratón sobre lienzo de pintura.
- Implementando el dibujo suave y el dibujo en el elemento.
Así que pensé que sería una buena idea hacer una pregunta de referencia, donde cada respuesta es wiki de la comunidad y contiene una explicación de exactamente una biblioteca de JavaScript / JavaScript puro sobre cómo pintar en el lienzo.
Estructura de respuestas
Las respuestas deben ser wiki de la comunidad y usar la siguiente plantilla:
## [Name of library](Link to project page)
### Simple example
A basic, complete example. That means it has to contain HTML
and JavaScript. You can start with this:
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<script type=''text/javascript'' src=''http://cdnjs.com/[your library]''></script>
<style type=''text/css''>
#sheet {
border:1px solid black;
}
</style>
<script type=''text/javascript''>
window.onload=function(){
// TODO: Adjust
}
</script>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
If possible, this example should work with both, mouse and touch events.
[JSFiddle](Link to code on jsfiddle.net)
This solution works with:
<!-- Please test it the following way: Write "Hello World"
Problems that you test this way are:
* Does it work at all?
* Are lines separated?
* Does it get slow when you write too much?
-->
* Desktop computers:
* [Browser + Version list]
* Touch devices:
* [Browser + Version list] on [Device name]
### Import / Export
Some explanations how to import / export user drawn images.
### Line smoothing
Explanations about how to manipulate the line the user draws.
This can include:
* Bézier curves
* Controlling thickness of lines
JavaScript simple
Ejemplo simple
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<style type=''text/css''>
#sheet {
border:1px solid black;
}
</style>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
<script type=''text/javascript''>
/*jslint browser:true */
"use strict";
var context = document.getElementById(''sheet'').getContext("2d");
var canvas = document.getElementById(''sheet'');
context = canvas.getContext("2d");
context.strokeStyle = "#ff0000";
context.lineJoin = "round";
context.lineWidth = 5;
var clickX = [];
var clickY = [];
var clickDrag = [];
var paint;
/**
* Add information where the user clicked at.
* @param {number} x
* @param {number} y
* @return {boolean} dragging
*/
function addClick(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
/**
* Redraw the complete canvas.
*/
function redraw() {
// Clears the canvas
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
for (var i = 0; i < clickX.length; i += 1) {
if (!clickDrag[i] && i == 0) {
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else if (!clickDrag[i] && i > 0) {
context.closePath();
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else {
context.lineTo(clickX[i], clickY[i]);
context.stroke();
}
}
}
/**
* Draw the newly added point.
* @return {void}
*/
function drawNew() {
var i = clickX.length - 1
if (!clickDrag[i]) {
if (clickX.length == 0) {
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
} else {
context.closePath();
context.beginPath();
context.moveTo(clickX[i], clickY[i]);
context.stroke();
}
} else {
context.lineTo(clickX[i], clickY[i]);
context.stroke();
}
}
function mouseDownEventHandler(e) {
paint = true;
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
if (paint) {
addClick(x, y, false);
drawNew();
}
}
function touchstartEventHandler(e) {
paint = true;
if (paint) {
addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, false);
drawNew();
}
}
function mouseUpEventHandler(e) {
context.closePath();
paint = false;
}
function mouseMoveEventHandler(e) {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
if (paint) {
addClick(x, y, true);
drawNew();
}
}
function touchMoveEventHandler(e) {
if (paint) {
addClick(e.touches[0].pageX - canvas.offsetLeft, e.touches[0].pageY - canvas.offsetTop, true);
drawNew();
}
}
function setUpHandler(isMouseandNotTouch, detectEvent) {
removeRaceHandlers();
if (isMouseandNotTouch) {
canvas.addEventListener(''mouseup'', mouseUpEventHandler);
canvas.addEventListener(''mousemove'', mouseMoveEventHandler);
canvas.addEventListener(''mousedown'', mouseDownEventHandler);
mouseDownEventHandler(detectEvent);
} else {
canvas.addEventListener(''touchstart'', touchstartEventHandler);
canvas.addEventListener(''touchmove'', touchMoveEventHandler);
canvas.addEventListener(''touchend'', mouseUpEventHandler);
touchstartEventHandler(detectEvent);
}
}
function mouseWins(e) {
setUpHandler(true, e);
}
function touchWins(e) {
setUpHandler(false, e);
}
function removeRaceHandlers() {
canvas.removeEventListener(''mousedown'', mouseWins);
canvas.removeEventListener(''touchstart'', touchWins);
}
canvas.addEventListener(''mousedown'', mouseWins);
canvas.addEventListener(''touchstart'', touchWins);
</script>
</body>
</html>
- El ancho de las líneas se puede controlar con
context.lineWidth
. - El color de las líneas se puede controlar con
strokeStyle
.
Esta solución funciona con:
- Computadores de escritorio:
- Cromo 33
- Firefox 28
- Dispositivos táctiles:
- Firefox 28 en Nexus 4
No funciona con
- Dispositivos táctiles:
- Chrome 34 / Opera 20 en Nexus 4 (ver issue )
Importación y exportación
La importación y exportación de la imagen se puede hacer importando / exportando clickX
, clickY
y clickDrag
.
Suavizado de linea
Eventualmente se puede hacer reemplazando lineTo()
con bezierCurveTo()
Fabric.js
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<script type=''text/javascript'' src=''http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js''></script>
<style type=''text/css''>
#sheet {
border:1px solid black;
}
</style>
<script type=''text/javascript''>
window.onload=function(){
var canvas = new fabric.Canvas(''sheet'');
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.width = 5;
canvas.freeDrawingBrush.color = "#ff0000";
}
</script>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
- El ancho de las líneas se puede controlar con
canvas.freeDrawingBrush.width
. - El color de las líneas se puede controlar con
canvas.freeDrawingBrush.color
.
Esta solución funciona con:
- Computadores de escritorio:
- Cromo 33
- Firefox 28
- Dispositivos táctiles:
- Chrome 34 en Nexus 4
- Opera 20 en Nexus 4
- Firefox 28 en Nexus 4
Importación y exportación
Solo es posible mediante la serialización del lienzo completo, ver Tutorial
Suavizado de linea
Se realiza de forma automática y parece que no es posible desactivarlo.
Paper.js
Ejemplo simple
<!DOCTYPE html>
<html>
<head>
<title>Paper.js example</title>
<script type=''text/javascript'' src=''http://paperjs.org/assets/js/paper.js''></script>
<style type=''text/css''>
#sheet {
border:1px solid black;
}
</style>
</head>
<body>
<script type="text/paperscript" canvas="sheet">
var path;
function onMouseDown(event) {
// If we produced a path before, deselect it:
if (path) {
path.selected = false;
}
// Create a new path and set its stroke color to black:
path = new Path({
segments: [event.point],
strokeColor: ''black'',
strokeWidth: 3
});
}
// While the user drags the mouse, points are added to the path
// at the position of the mouse:
function onMouseDrag(event) {
path.add(event.point);
}
// When the mouse is released, we simplify the path:
function onMouseUp(event) {
path.simplify();
}
</script>
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
- El ancho de las líneas se puede controlar con
strokeWidth
. - El color de las líneas se puede controlar con
strokeColor
.
Esta solución funciona con:
- Computadores de escritorio:
- Cromo 33
Importación y exportación
?
Suavizado de linea
El suavizado de líneas se puede hacer ajustando path.simplify();
.
EaselJs
Ejemplo simple
A basic, complete example. That means it has to contain HTML
and JavaScript. You can start with this:
<!DOCTYPE html>
<html>
<head>
<title>EaselJS example</title>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/EaselJS/0.7.1/easeljs.min.js"></script>
<script>
var canvas, stage;
var drawingCanvas;
var oldPt;
var oldMidPt;
var color;
var stroke;
var index;
function init() {
if (window.top != window) {
document.getElementById("header").style.display = "none";
}
canvas = document.getElementById("sheet");
index = 0;
//check to see if we are running in a browser with touch support
stage = new createjs.Stage(canvas);
stage.autoClear = false;
stage.enableDOMEvents(true);
createjs.Touch.enable(stage);
createjs.Ticker.setFPS(24);
drawingCanvas = new createjs.Shape();
stage.addEventListener("stagemousedown", handleMouseDown);
stage.addEventListener("stagemouseup", handleMouseUp);
stage.addChild(drawingCanvas);
stage.update();
}
function stop() {}
function handleMouseDown(event) {
color = "#ff0000";
stroke = 5;
oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
oldMidPt = oldPt;
stage.addEventListener("stagemousemove" , handleMouseMove);
}
function handleMouseMove(event) {
var midPt = new createjs.Point(oldPt.x + stage.mouseX>>1, oldPt.y+stage.mouseY>>1);
drawingCanvas.graphics.clear().setStrokeStyle(stroke, ''round'', ''round'').beginStroke(color).moveTo(midPt.x, midPt.y).curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPt.x;
oldMidPt.y = midPt.y;
stage.update();
}
function handleMouseUp(event) {
stage.removeEventListener("stagemousemove" , handleMouseMove);
}
</script>
</head>
<body onload="init();">
<canvas id="sheet" width="400" height="400"></canvas>
</body>
</html>
Las partes interesantes en la documentación son:
- EaselJS : Un punto de partida para entrar en EaselJS.
- Clase de escenario
Esta solución funciona con:
- Computadores de escritorio:
- Cromo 33
- Firefox 28
- Dispositivos táctiles:
- Chrome 34 / Firefox 28 / Opera 20 en Nexus 4
Importación y exportación
?
Suavizado de linea
?
(Descargo de responsabilidad: escribí esta biblioteca)
Scrawl.js
Ejemplo simple
<!DOCTYPE html>
<html>
<head>
<title>Simple example</title>
<style type=''text/css''>
#sheet {border:1px solid black;}
</style>
</head>
<body>
<canvas id="sheet" width="400" height="400"></canvas>
<script src="http://scrawl.rikweb.org.uk/js/scrawlCore-min.js"></script>
<script>
var mycode = function(){
//define variables
var myPad = scrawl.pad.sheet,
myCanvas = scrawl.canvas.sheet,
sX, sY, here,
drawing = false,
currentSprite = false,
startDrawing,
endDrawing;
//event listeners
startDrawing = function(e){
drawing = true;
currentSprite = scrawl.newShape({
start: here,
lineCap: ''round'',
lineJoin: ''round'',
method: ''draw'',
lineWidth: 4,
strokeStyle: ''red'',
data: ''l0,0 '',
});
sX = here.x;
sY = here.y;
if(e){
e.stopPropagation();
e.preventDefault();
}
};
myCanvas.addEventListener(''mousedown'', startDrawing, false);
endDrawing = function(e){
if(currentSprite){
currentSprite = false;
}
drawing = false;
if(e){
e.stopPropagation();
e.preventDefault();
}
};
myCanvas.addEventListener(''mouseup'', endDrawing, false);
//animation object
scrawl.newAnimation({
fn: function(){
//get current mouse position
here = myPad.getMouse();
if(here.active){
if(drawing){
if(here.x !== sX || here.y !== sY){
//extend the line
currentSprite.set({
data: currentSprite.data+'' ''+(here.x - sX)+'',''+(here.y - sY),
});
sX = here.x;
sY = here.y;
}
}
}
else{
//stop drawing if mouse leaves canvas area
if(currentSprite){
endDrawing();
}
}
//update display
scrawl.render();
},
});
};
//Scrawl is modular - load additional modules
scrawl.loadModules({
path: ''js/'',
modules: [''animation'', ''shape''],
callback: function(){
window.addEventListener(''load'', function(){
scrawl.init(); //start Scrawl
mycode(); //run code
}, false);
},
});
</script>
</body>
</html>
Esta solución funciona con:
- versiones recientes de IE, Chrome, Firefox, Opera (escritorio)
- (no probado en dispositivos móviles / táctiles)
Añadiendo soporte táctil
- (intente agregar una biblioteca táctil dedicada como Hammer.js?)
Importación y exportación
- Scrawl tiene soporte experimental para guardar y cargar cadenas JSON
- Página tutorial: cargar y guardar
Alisado de líneas y otras manipulaciones de sprites.
- los datos de línea se guardan internamente como un valor SVGTiny Path.d: cualquier algoritmo que pueda tomar datos de línea en ese formato y sin problemas debe funcionar
- Los atributos de línea (grosor, color, posicionamiento, rotación, etc.) se pueden configurar y animar.
Aquí, prueba mi lienzo de dibujo libre y borra.
https://jsfiddle.net/richardcwc/d2gxjdva/
//Canvas
var canvas = document.getElementById(''canvas'');
var ctx = canvas.getContext(''2d'');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;
var tooltype = ''draw'';
//Mousedown
$(canvas).on(''mousedown'', function(e) {
last_mousex = mousex = parseInt(e.clientX-canvasx);
last_mousey = mousey = parseInt(e.clientY-canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on(''mouseup'', function(e) {
mousedown = false;
});
//Mousemove
$(canvas).on(''mousemove'', function(e) {
mousex = parseInt(e.clientX-canvasx);
mousey = parseInt(e.clientY-canvasy);
if(mousedown) {
ctx.beginPath();
if(tooltype==''draw'') {
ctx.globalCompositeOperation = ''source-over'';
ctx.strokeStyle = ''black'';
ctx.lineWidth = 3;
} else {
ctx.globalCompositeOperation = ''destination-out'';
ctx.lineWidth = 10;
}
ctx.moveTo(last_mousex,last_mousey);
ctx.lineTo(mousex,mousey);
ctx.lineJoin = ctx.lineCap = ''round'';
ctx.stroke();
}
last_mousex = mousex;
last_mousey = mousey;
//Output
$(''#output'').html(''current: ''+mousex+'', ''+mousey+''<br/>last: ''+last_mousex+'', ''+last_mousey+''<br/>mousedown: ''+mousedown);
});
//Use draw|erase
use_tool = function(tool) {
tooltype = tool; //update
}
canvas {
cursor: crosshair;
border: 1px solid #000000;
}
<canvas id="canvas" width="800" height="500"></canvas>
<input type="button" value="draw" onclick="use_tool(''draw'');" />
<input type="button" value="erase" onclick="use_tool(''erase'');" />
<div id="output"></div>