最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to restore a canvas to status saved in another function? - Stack Overflow

programmeradmin4浏览0评论

I want to save the status of a canvas in a function draw1(), and restore it in another function draw2(). So I wrote following code. But it doesn't work.

<canvas id="canvas" style="width:500px; height:500px;" height="500" width="500"></canvas>
ctx = document.querySelector('#canvas').getContext('2d');
function draw1(){
   ctx.save();
   ctx.fillRect(25,25,100,100);
}
function draw2(){
   ctx.restore();
}
draw1();
draw2();

I guess the reason is that the status is saved in the call stack. So after the function returns, the saving state is also cleared.

Is there other way to implement my need?

UPD: The background is that I want to implement a simple animation, most of which are static. I expect to use setInterval() to execute a drawing function draw(). In draw(), restore the canvas back first and draw the remaining dynamic part.

I want to save the status of a canvas in a function draw1(), and restore it in another function draw2(). So I wrote following code. But it doesn't work.

<canvas id="canvas" style="width:500px; height:500px;" height="500" width="500"></canvas>
ctx = document.querySelector('#canvas').getContext('2d');
function draw1(){
   ctx.save();
   ctx.fillRect(25,25,100,100);
}
function draw2(){
   ctx.restore();
}
draw1();
draw2();

I guess the reason is that the status is saved in the call stack. So after the function returns, the saving state is also cleared.

Is there other way to implement my need?

UPD: The background is that I want to implement a simple animation, most of which are static. I expect to use setInterval() to execute a drawing function draw(). In draw(), restore the canvas back first and draw the remaining dynamic part.

Share Improve this question asked May 24, 2013 at 5:24 konjackonjac 7878 silver badges15 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 6

If I understand right you only need to draw some static objects once and then draw the animated objects each frame.

First off all, you pletely misunderstood the save and restore methods and Michael Geary showed you why. Also markE teaches you the toDataURL method for take snapshots of your canvas at any time and save into an image object. It's a powerful feature, but isn't what you really want for simple animations.

So, how do I create animations with static and dynamic objects?

How to create animations with static and dynamic objects

There's two main options:

  1. Using one single canvas and draw all objects (static and dynamic) each frame of your animation, which probably is not the best for you since most of your objects are static.
  2. Have a canvas for static objects and another canvas for dynamic objects. Using this technic you only need to draw the static objects once and forget it there (no need for "restore" your canvas) and we perform the animations (drawing dynamic objects each frame) in a separated canvas.

I think the best for you will be option 2. Ok, but how we setup those canvases?

Using multiple canvases as layers

Use CSS to set all the canvases to an absolute position of (0,0) inside our parent div tag.

Also use CSS to set the z-index of our canvases. The z-index property specifies the stack order of an element. Items with lower z-index values go behind items with higher z-index values.

Now that we properly define our canvases, let's play!

Demo

I made a jsFiddle to show you how to acplish the desired animations.

Check the Fiddle

and the code used in that fiddle:

HTML:

<div id="canvasesdiv">
    <canvas id="static" width=400 height=400>This text is displayed if your browser does not support HTML5 Canvas</canvas>
    <canvas id="dynamic" width=400 height=400>This text is displayed if your browser does not support HTML5 Canvas</canvas>
</div>

CSS:

#canvasesdiv {
    position:relative;
    width:400px;
    height:300px;
}
#static {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 1;
}
#dynamic {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 2;
}

Javascript:

// static canvas
var static = document.getElementById("static");
var staticCtx = static.getContext("2d");

// dynamic canvas
var dynamic = document.getElementById("dynamic");
var dynamicCtx = dynamic.getContext("2d");

// animation status
var FPS = 30;
var INTERVAL = 1000 / FPS;

// our background
var myStaticObject = {
    x: 0,
    y: 0,
    width: static.width,
    height: static.height,
    draw: function () {
        staticCtx.fillStyle = "rgb(100, 100, 0)";
        staticCtx.fillRect(0, 0, static.width, static.height);
    }
};

// our bouncing rectangle
var myDynamicObject = {
    x: 30,
    y: 30,
    width: 50,
    height: 50,
    gravity: 0.98,
    elasticity: 0.90,
    friction: 0.1,
    velX: 10,
    velY: 0,
    bouncingY: false,
    bouncingX: false,
    draw: function () {   // example of dynamic animation code
        // clear the last draw of this object
        dynamicCtx.clearRect(this.x - 1, this.y - 1, this.width + 2, this.height + 2);            
        // pute gravity
        this.velY += this.gravity;
        // bounce Y
        if (!this.bouncingY && this.y >= dynamic.height - this.height) {
            this.bouncingY = true;
            this.y = dynamic.height - this.height;
            this.velY = -(this.velY * this.elasticity);
        } else {
            this.bouncingY = false;
        }
        // bounce X
        if (!this.bouncingX && (this.x >= dynamic.width - this.width) || this.x <= 0) {
            this.bouncingX = true;
            this.x = (this.x < 0 ? 0 : dynamic.width - this.width);
            this.velX = -(this.velX * this.elasticity);
        } else {
            this.bouncingX = false;
        }
        // pute new position
        this.x += this.velX;
        this.y += this.velY;            
        // render the object
        dynamicCtx.fillStyle = "rgb(150, 100, 170)";
        dynamicCtx.fillRect(this.x, this.y, this.width, this.height);
    }
};

function drawStatic() {
    myStaticObject.draw();
    // you can add more static objects and draw here
}

function drawDynamic() {        
    myDynamicObject.draw();
    // you can add more dynamic objects and draw here
}

function animate() {
    setInterval(function () {
        // only need to redraw dynamic objects
        drawDynamic();
    }, INTERVAL);
}

drawStatic(); // draw the static objects
animate(); // entry point for animated (dynamic) objects

You can save and reload the pixels on the canvas by using canvas.toDataURL()

Here's the save:

dataURL=canvas.toDataURL();

Here's the reload:

var image=new Image();
image.onload=function(){
    ctx.drawImage(image,0,0);
}
image.src=dataURL;

If you need to save the context attributes (fillStyle, etc), you'll have to save those in an object and reload them into the context when you reload the pixels.

If you need to save the transforms then you will have to create a transform matrix (an array of 6 numerics) Then you will need to track each transform you do by manipulating your transform matrix. See this blog post: http://blog.safaribooksonline./2012/04/26/html5-canvas-games-tracking-transformation-matrices/

Here's code and a Fiddle: http://jsfiddle/m1erickson/btmLE/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery./jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    ctx.fillRect(25,25,100,100);

    var dataURL;

    $("#save").click(function(){
        dataURL=canvas.toDataURL();
        ctx.clearRect(0,0,canvas.width,canvas.height);
    });

    $("#reload").click(function(){
        var image=new Image();
        image.onload=function(){
            ctx.drawImage(image,0,0);
        }
        image.src=dataURL;
    });

}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=300 height=300></canvas><br>
    <button id="save">Save</button>
    <button id="reload">Reload</button>
</body>
</html>

No, .save() and .restore() do not save the canvas state in the JavaScript call stack. Returning from a JavaScript function won't affect that state—it's saved pletely outside that world, in the canvas itself.

But I think you may be expecting these functions to do something other than what they actually do.

Here's a fiddle with your code.

It has a black rectangle, as expected from the .fillRect() call.

Were you thinking that the .restore() call would make the black rectangle go away? That's not what the function does. It doesn't restore the canvas bitmap to its previous state, only other canvas settings such as the clipping region, the stroke and fill styles, etc.

Here's an article that explains some of this.

If you want to save the actual bitmap you'll need to use other means to do that, perhaps using .getImageDataHD() and .setImageDataHD() - I'm not sure off the top of my head what would be best.

发布评论

评论列表(0)

  1. 暂无评论