A word of warning!
Saving the whole canvas as an image for undo/redo is memory intensive and a performance killer.
However, your idea of progressively saving the user’s drawings in an array is still a good idea.
Instead of saving the whole canvas as an image, just create an array of points to record every mousemove the user makes as they are drawing. This is your “drawing array” that can be used to fully redraw your canvas.
Whenever the user drags the mouse they are creating a polyline (a group of connected line segments). When the user drags to create a line, save that mousemove point to your drawing array and extend their polyline to the current mousemove position.
function handleMouseMove(e) {
// calc where the mouse is on the canvas
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// if the mouse is being dragged (mouse button is down)
// then keep drawing a polyline to this new mouse position
if (isMouseDown) {
// extend the polyline
ctx.lineTo(mouseX, mouseY);
ctx.stroke();
// save this x/y because we might be drawing from here
// on the next mousemove
lastX = mouseX;
lastY = mouseY;
// Command pattern stuff: Save the mouse position and
// the size/color of the brush to the "undo" array
points.push({
x: mouseX,
y: mouseY,
size: brushSize,
color: brushColor,
mode: "draw"
});
}
}
If the user wants to “undo”, just pop the last point off the drawing array:
function undoLastPoint() {
// remove the last drawn point from the drawing array
var lastPoint=points.pop();
// add the "undone" point to a separate redo array
redoStack.unshift(lastPoint);
// redraw all the remaining points
redrawAll();
}
Redo is logically more tricky.
The simplest Redo is when the user can only redo immediately after an undo. Save each “undo” point in your separate “redo” array. Then if the user wants to redo, you can just add the redo bits back to the to the main array.
The complication is if you let the user “redo” after they have done more drawing.
For example, you could end up with a dog with 2 tails: a newly drawn tail and a second “redo” tail !
So if you allow redo’s after additional drawing, you would need a way to keep the user from being confused during redo. Matt Greer’s idea of “layering” redos is one good way. Just alter that idea by saving the redo points, not the entire canvas image. Then the user could toggle the redo on/off to see if they would like to keep their redo.
Here is an example of using an undo array I created for a previous question: Drawing to canvas like in paint
Here is that code and a Fiddle: http://jsfiddle.net/m1erickson/AEYYq/
Drag to draw. Use buttons to change lineWidth/color