2010-06-23 17 views
10

Tengo una aplicación de bocetos en HTML5 y Javascript, y me preguntaba cómo crearía un botón Deshacer, por lo que podría deshacer lo último que haya dibujado. ¿Alguna idea?Cómo agregar la funcionalidad de deshacer a HTML5 Canvas?

+1

Consulte el [patrón de comando] (http://en.wikipedia.org/wiki/Command_pattern) – Anurag

+1

¿por qué tiene java y object-c en las etiquetas? –

Respuesta

13

Debe almacenar todas las modificaciones en una estructura de datos. A continuación, puede eliminar la última modificación si el usuario desea deshacer. Luego vuelve a pintar todas las operaciones de dibujo desde su estructura de datos.

+0

Consulte http://whiffdoc.appspot.com/tests/schema/diagram para ver un ejemplo de cómo hacerlo; en este caso, almacenando una estructura de datos JSON que represente la información del lienzo en un elemento DIV invisible. (siga el enlace de la página a la fuente) –

+1

@AaronWatters, este enlace me dirige a 404. ¿Tiene el enlace actualizado? – link2pk

3

En http://arthurclemens.github.io/Javascript-Undo-Manager/ Tengo un ejemplo funcional de deshacer con un elemento canvas. Cuando realiza una modificación, alimenta al administrador de deshacer los métodos de deshacer y rehacer. El seguimiento de la posición en la pila de deshacer se realiza automáticamente. El código fuente está en Github.

+0

¡su ejemplo se ve bien! Gracias por compartir –

+0

se ve bien en Chrome, pero no funciona en Firefox – waspinator

+0

He arreglado un par de pequeños errores de dibujo en la demostración. –

1

La otra opción, si necesita manipular objetos es convertir su lienzo a SVG utilizando una biblioteca que preserve el Canvas API evitando una reescritura.

Al menos uno de estos biblioteca existe en este momento (noviembre de 2011): SVGKit

Una vez que tenga SVG, es mucho más fácil de quitar objetos y mucho más sin la necesidad de volver a dibujar todo el lienzo.

+2

O puede simplemente usar la biblioteca de abstracción de lienzo, como [fabric.js] (http://kangax.github.com/fabric.js/) para poder trabajar con objetos en lienzo mediante programación. – kangax

+0

Excelente @kangax. Tengo curiosidad, ¿cómo crees que fabric.js se podría comparar con SVGKit en términos de rendimiento cuando se trata de editar objetos en lienzos con cientos de nodos? –

+0

No estoy seguro de cómo se compara con SVGKit, pero cuando se trata de una gran cantidad de objetos, el lienzo tiende a ser más eficiente que SVG. Eche un vistazo a algunos benchmarks comparando Raphael (basado en SVG) y fabric (basado en lienzo) - http://fabricjs.com/benchmarks/ – kangax

0

Aquí hay una solución que funciona para mí. Lo probé en las últimas versiones de Firefox y Chrome y funciona muy bien en esos dos navegadores.

var isFirefox = typeof InstallTrigger !== 'undefined'; 
var ctx = document.getElementById('myCanvas').getContext("2d"); 
var CanvasLogBook = function() { 
    this.index = 0; 
    this.logs = []; 
    this.logDrawing(); 
}; 
CanvasLogBook.prototype.sliceAndPush = function(imageObject) { 
    var array; 
    if (this.index == this.logs.length-1) { 
     this.logs.push(imageObject); 
     array = this.logs; 
    } else { 
     var tempArray = this.logs.slice(0, this.index+1); 
     tempArray.push(imageObject); 
     array = tempArray; 
    } 
    if (array.length > 1) { 
     this.index++; 
    } 
    return array; 
}; 
CanvasLogBook.prototype.logDrawing = function() { 
    if (isFirefox) { 
     var image = new Image(); 
     image.src = document.getElementById('myCanvas').toDataURL(); 
     this.logs = this.sliceAndPush(image); 
    } else { 
     var imageData = document.getElementById('myCanvas').toDataURL(); 
     this.logs = this.sliceAndPush(imageData); 
    } 
}; 
CanvasLogBook.prototype.undo = function() { 
    ctx.clearRect(0, 0, $('#myCanvas').width(), $('#myCanvas').height()); 
    if (this.index > 0) { 
     this.index--; 
     this.showLogAtIndex(this.index); 
    } 
}; 
CanvasLogBook.prototype.redo = function() { 
    if (this.index < this.logs.length-1) { 
     ctx.clearRect(0, 0, $('#myCanvas').width(), $('#myCanvas').height()); 
     this.index++; 
     this.showLogAtIndex(this.index); 
    } 
}; 
CanvasLogBook.prototype.showLogAtIndex = function(index) { 
    ctx.clearRect(0, 0, $('#myCanvas').width(), $('#myCanvas').height()); 
    if (isFirefox) { 
     var image = this.logs[index]; 
     ctx.drawImage(image, 0, 0); 
    } else { 
     var image = new Image(); 
     image.src = this.logs[index]; 
     ctx.drawImage(image, 0, 0); 
    } 
}; 
var canvasLogBook = new CanvasLogBook(); 

Así que cada vez que dibuje cualquier cosa que existe después de la función de ejecución canvasLogBook.logDrawing() para guardar una instantánea de la tela y luego se le puede llamar canvasLogBook.undo() para deshacer y canvasLogBook.redo() Rehacer.

+0

Esta sería una buena respuesta si no dependiera de jQuery y si se deshace de 'isFirefox' boolean :) – A1rPun