开始的时候,我很难理解画布2d上下文的save()restore()方法的用途和用法;2d上下文会维持一个绘图状态堆栈,这个堆栈用于保存绘图状态。它非常简单,这里有一些例子可以帮助你更好地理解它。

让我们看看save()和restore()的官方定义:

每个上下文维护一组绘图状态。绘图状态包括:

  • 当前的变换矩阵。
  • 当前剪辑区域。
  • 以下属性的当前值:strokeStyle、fillStyle、globalAlpha、lineWidth、lineCap、lineJoin、miterLimit、shadowOffsetX、shadowOffsetY、shadowBlur、shadowColor、globalCompositeOperation、font、textAlign、textBaseline。

当前路径和当前位图不是绘图状态的一部分。当前路径是持久的,只能使用beginPath()方法重置。当前位图是画布的属性,而不是上下文的属性。
context.save()将当前状态推送到堆栈上。
context.restore()弹出堆栈的顶层状态,将上下文还原为该状态。

由于一个画布只能有一个2d上下文,所以Save和restore可以用作多种情况的解决方案。最常见的用途之一是变换。

save()和restore()用于变换的例子。

执行变换时,整个上下文的坐标系都将被转换。变换后,通常希望坐标系在下一步中恢复正常。通过使用另一个变换来反转变换是一件冒险的事情,很容易引入一些小错误,这些错误会很快累积起来。只需将最初坐标系以及绘图状态保存起来,然后在我们进行变换后,恢复最初坐标系以及绘图状态。我们还是来看一些图吧。

我们调用context.save()将当前绘图状态推送到绘图状态堆栈中。

canvas save restore

然后我们执行变换(旋转、 缩放、 自定义变换),坐标系状态如下:

canvas变换

变换后,画出我们的形状(一个2x2的正方形),context.fillRect(1,1,2,2)。执行之后的结果:

canvas

现在我们想绘制更多的图形,但不希望应用当前的变换,所以我们调用restore()并从绘图状态堆栈中弹出最后保存的绘图状态。

canvas restore状态

请注意,已经绘制的形状没有更改,只是改变了未来绘图时的绘图状态。

一个完整的例子

下面是一个超级简单的示例来说明绘图状态堆栈如何与save()和restore()一起工作。

<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Canvas测试</title>
</head>
<body>
<header> </header>
<nav> </nav>
<section>

<div>
<canvas id="canvas" width="320" height="200">
不支持canvas
</canvas>
</div>

<script type="text/javascript">
var canvas;
var ctx;

function init() {
        canvas = document.getElementById("canvas");
        ctx = canvas.getContext("2d");
        draw();
}


function draw() {

        ctx.fillStyle = '#FA6900';
        ctx.shadowOffsetX = 5;
        ctx.shadowOffsetY = 5;
        ctx.shadowBlur    = 4;
        ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';
        ctx.fillRect(0,0,15,150);
        ctx.save();

        ctx.fillStyle = '#E0E4CD';
        ctx.shadowOffsetX = 10;
        ctx.shadowOffsetY = 10;
        ctx.shadowBlur    = 4;
        ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';
        ctx.fillRect(30,0,30,150);
        ctx.save();

        ctx.fillStyle = '#A7DBD7';
        ctx.shadowOffsetX = 15;
        ctx.shadowOffsetY = 15;
        ctx.shadowBlur    = 4;
        ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';
        ctx.fillRect(90,0,45,150);
        ctx.save();

        ctx.restore();
        ctx.beginPath();
        ctx.arc(185, 75, 22, 0, Math.PI*2, true);
        ctx.closePath();
        ctx.fill();

        ctx.restore();
        ctx.beginPath();
        ctx.arc(260, 75, 15, 0, Math.PI*2, true);
        ctx.closePath();
        ctx.fill();

        ctx.restore();
        ctx.beginPath();
        ctx.arc(305, 75, 8, 0, Math.PI*2, true);
        ctx.closePath();
        ctx.fill();
}

init();
</script>
</section>
<aside> </aside>
<footer> </footer>
</body>
</html>

首先,我们绘制一个矩形,设置形状的fillStyle和shadow属性。然后我们调用ctx.save()将当前当前绘图状态添加到堆栈中。

    ctx.fillStyle = '#FA6900'; 
    ctx.shadowOffsetX = 5;
    ctx.shadowOffsetY = 5;
    ctx.shadowBlur    = 4;
    ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';      
    ctx.fillRect(0,0,15,150);
    ctx.save();

'canvas save'

当前绘图状态堆栈:

canvas


然后我们绘制另一个矩形,为我们的形状设置fillStyle和shadow属性,并调用ctx.save()将当前的绘图状态设置添加到堆栈中。

    ctx.fillStyle = '#E0E4CD'; 
    ctx.shadowOffsetX = 10;
    ctx.shadowOffsetY = 10;
    ctx.shadowBlur    = 4;
    ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';      
    ctx.fillRect(30,0,30,150);
    ctx.save(); 

canvas

目前状态堆栈里面有2个状态了:

canvas状态堆栈


我们绘制另一个矩形,设置形状的填充样式和阴影属性。再次调用ctx.save()。

    ctx.fillStyle = '#A7DBD7'; 
    ctx.shadowOffsetX = 15;
    ctx.shadowOffsetY = 15;
    ctx.shadowBlur    = 4;
    ctx.shadowColor   = 'rgba(204, 204, 204, 0.5)';      
    ctx.fillRect(90,0,45,150);
    ctx.save();

canvas

目前状态堆栈里面有3个状态:

canvas status stack


最好我们调用来ctx.restore()从堆栈顶部弹出状态,然后使用这些状态绘制3个圆。

渝ICP备20006974号-2