如何将画布恢复到另一个函数中保存的状态?

时间:2013-05-24 05:24:50

标签: javascript canvas html5-canvas

我想在函数 draw1()中保存画布的状态,并在另一个函数 draw2()中恢复它。所以我写了下面的代码。但它不起作用。

<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();

我猜原因是状态保存在调用堆栈中。因此,在函数返回后,还会清除保存状态。

还有其他方法可以实现我的需求吗?

UPD:背景是我想要实现一个简单的动画,其中大部分都是静态的。我希望使用 setInterval()来执行绘图函数 draw()。在 draw()中,首先恢复画布并绘制剩余的动态部分。

3 个答案:

答案 0 :(得分:5)

如果我理解正确你只需要绘制一些静态对象,然后每帧绘制动画对象。

首先,你完全误解了saverestore方法,Michael Geary告诉你原因。此外,markE会教您toDataURL方法,以便随时拍摄画布的快照并保存到图像对象中。这是一个强大的功能,但不是你真正想要的简单动画。

那么,如何使用静态和动态对象创建动画?

如何使用静态和动态对象创建动画

有两个主要选择:

  1. 使用单个画布并绘制动画的每个帧(静态和动态),这可能不是最适合您的,因为大多数对象都是静态的。
  2. 拥有静态对象的画布和动态对象的另一个画布。使用这种技术,你只需要绘制一次静态对象并忘记它(不需要“恢复”你的画布),我们在一个单独的画布中执行动画(每帧绘制动态对象)。
  3. 我认为最好的选择是2.好的,但我们如何设置这些画布?

    使用多个画布作为图层

    使用CSS将所有画布设置为父div标签内的绝对位置(0,0)。

    还可以使用CSS来设置画布的z-index。 z-index属性指定元素的堆栈顺序。具有较低z-index值的项目将落后于具有较高z-index值的项目。

    现在我们正确定义了我们的画布,让我们玩吧!

    演示

    我制作了一个jsFiddle来向您展示如何完成所需的动画。

    Check the 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);            
            // compute 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;
            }
            // compute 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
    

答案 1 :(得分:2)

您可以使用canvas.toDataURL()

在画布上保存和重新加载像素

这是保存:

dataURL=canvas.toDataURL();

这是重装:

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

如果您需要保存上下文属性(fillStyle等),则必须将它们保存在对象中,并在重新加载像素时将它们重新加载到上下文中。

如果需要保存变换,则必须创建变换矩阵(由6个数字组成的数组)然后,您需要通过操纵变换矩阵来跟踪每个变换。 请参阅此博文:http://blog.safaribooksonline.com/2012/04/26/html5-canvas-games-tracking-transformation-matrices/

此处的代码和小提琴:http://jsfiddle.net/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.com/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>

答案 2 :(得分:1)

不,.save().restore()不会将画布状态保存在JavaScript调用堆栈中。从JavaScript函数返回不会影响该状态 - 它在画布本身中完全保存在该世界之外。

但我认为你可能期望这些功能可以做他们实际做的事情。

这是a fiddle with your code

它有一个黑色矩形,正如.fillRect()调用所预期的那样。

您是否认为.restore()调用会使黑色矩形消失?这不是功能的作用。它不会将画布位图恢复到以前的状态,只会将剪裁区域,笔触和填充样式等其他画布设置恢复。

这是an article that explains some of this

如果你想保存实际的位图,你需要使用其他方法来做到这一点,也许使用.getImageDataHD().setImageDataHD() - 我不确定是什么意思最好。