Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save Multiple States and Restore to Each One

HTML5 <canvas> elements have a sort of save state mechanism, by repeatedly calling cxt.save() and cxt.restore(), where cxt is the <canvas>'s "context", gotten by, for example, calling document.getElementById('my-canvas'). However, I have not seen a way to save/restore to more than one state at a time. It seems that <canvas> has access to multiple states, as calling restore multiple times restores each respective "last" saved state.

Is there a way to jump back to before the "last" state without calling restore multiple times? Also, is there a way to go "forward" through the states? This would probably be easier to do if we could give each state a name, but I could work with ID's, if I needed to. In the end it would probably look similar to Adobe Photoshop's "layers" concept.

I would also take an answer that saves each state to a different variable, or that can copy the context without creating a whole new <canvas>. Copying functions like angular.copy do not seem to work with contexts, possibly because they are not technically objects; I believe they inherit from interfaces.

Finally, is this efficient? I am aware of how graphics-intensive <canvas> is, how taxing it can be on the client's computer. Is there a better way of manipulating the context without disrupting other parts of the canvas than implementing a layering system?

like image 212
trysis Avatar asked Aug 12 '15 18:08

trysis


1 Answers

Some background:

context.save and context.restore save and restore the context state. Context state includes styling, transformations, compositing, etc.

.save will push the current state on top of a stack. .restore will pop the last added state off the top of a stack. So they act as a last-in-first-out stack of states.

You can do multiple .save which will push multiple states onto the stack. Then you can pop multiple states off the stack by doing multiple .restore.

Since .save will save all context state, saves & restores are relatively expensive operations.

An example of multiple save/restore:

context.fillStyle='red';
context.fillRect(0,0,10,10);    // red rectangle
context.save();                 // fillStyle=='red'
context.fillStyle='blue';       // fillStyle='blue'
context.fillRect(0,0,10,10);    // blue rectangle
context.save();                 // fillStyle=='blue'
context.fillStyle='green';      // fillStyle=='green'
context.fillRect(0,0,10,10);    // green rectangle
context.restore();              // fillStyle='blue';
context.fillRect(0,0,10,10);    // blue rectangle
context.restore();              // fillStyle='red'
context.fillRect(0,0,10,10);    // red rectangle

Answer#1: No, there is no way to regress to prior saved states without executing multiple restores. The states are saved on a stack so unlike an array of states, you cannot "jump" to states[2].

Answer#2: In practice, it's much more common to not use save/restore, but rather to use javascript objects to store the least state info as required.

For example:

// put various fillStyles in a fills object
var fills={};
fills.red='red';
fills.green='green';
fills.blue='blue';

// create a function that draws a rect with specified fillStyle
function styledRect(x,y,w,h,fill){
    var priorFill=context.fillStyle;
    context.fillStyle=fill;
    context.fillRect(x,y,w,h);
    context.fillStyle=priorFill;
}

// use the fills object to control the fillStyle "state"
styledRect(0,0,10,10,fills.red);

A more complex "state" object might look like this:

buttonStyles={};
buttonStyles.normal={ font:'12px verdana', fill:'black' };
buttonStyles.warning={font:'12px italic verdana', fill:'orange' };
buttonStyles.danger={ font:'14px italic verdana', fill:'red' };

// example usage
someButtonDrawingFunction("You're in danger!",buttonStyles.danger);
like image 100
markE Avatar answered Nov 11 '22 06:11

markE