【问题标题】:Save Multiple States and Restore to Each One保存多个状态并恢复到每个状态
【发布时间】:2015-08-12 18:17:56
【问题描述】:

HTML5 <canvas> 元素有一种保存状态机制,通过重复调用cxt.save()cxt.restore(),其中cxt<canvas> 的“上下文”,例如通过调用@ 987654326@。但是,我还没有看到一次保存/恢复到多个状态的方法。 <canvas> 似乎可以访问多个状态,因为多次调用 restore 会恢复每个相应的“最后”保存状态。

有没有办法在不多次调用restore 的情况下跳回到“最后”状态之前?另外,有没有办法通过各州“前进”?如果我们可以为每个州命名,这可能会更容易,但如果需要,我可以使用 ID。最终它可能看起来类似于 Adob​​e Photoshop 的“图层”概念。

我还会选择将每个状态保存到不同变量的答案,或者可以在不创建全新<canvas> 的情况下复制上下文。像angular.copy 这样的复制函数似乎不适用于上下文,可能是因为它们在技术上不是对象;我相信它们继承自接口。

最后,这是否有效?我知道<canvas> 的图形密集程度如何,它在客户计算机上的负担有多大。有没有比实现分层系统更好的方法来操作上下文而不破坏画布的其他部分?

【问题讨论】:

    标签: javascript html canvas


    【解决方案1】:

    一些背景:

    context.savecontext.restore 保存和恢复上下文状态。上下文状态包括样式、转换、合成等。

    .save 会将当前状态推送到堆栈顶部。 .restore 将从堆栈顶部弹出最后添加的状态。因此,它们充当后进先出的状态堆栈。

    您可以执行多个.save,这会将多个状态推送到堆栈上。然后,您可以通过执行多个.restore 将多个状态从堆栈中弹出。

    由于.save 将保存所有上下文状态,保存和恢复是相对昂贵的操作。

    多次保存/恢复的示例:

    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
    

    答案#1:不,如果不执行多次恢复,就无法恢复到之前保存的状态。状态保存在堆栈中,因此与状态数组不同,您不能“跳转”到状态[2]。

    答案#2:在实践中,更常见的是不使用保存/恢复,而是使用 javascript 对象来存储所需的最少状态信息

    例如:

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

    更复杂的“状态”对象可能如下所示:

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

    【讨论】:

    • 这基本上就是我正在做的事情,但是现在我有循环,循环内的循环,看起来这会变得更加复杂。另外,像stroke()fill() 这样的函数不是基于当前状态工作,并且即使它们已经被绘制,也不会为使用相同状态创建的所有形状设置样式?你如何在不使用不同状态的情况下解决这个问题?
    • 我在我的回答中更新了示例 styledRect 函数,以显示您将如何“手动”保存正在更改的最小状态并“手动”恢复以前的状态。是的,所有笔画+填充仅适用于当前状态,每个 beginPath 只能获得 1 个状态(最后声明的状态总是获胜)。如果您想要 2 个不同的笔触或填充,那么您必须执行 2 个单独的路径。 ;-)
    • 好的,所以使用最后开始路径的值,而不是最后声明的状态。关于画布,我还有很多东西要学。
    • 相反! ;-)。如果您 beginPath 并声明 5 个不同的 strokeStyle,那么当您最终调用 stroke() 时,只会使用最后一个 strokeStyle。每个 beginPath 只需获得 1 个样式,最后声明的样式总是获胜。 fillRect & strokeRect 实际上是“幕后”的路径命令,因此只会使用 fillRect 或 strokeRect 之前的最后一个 strokeStyle / fillStyle。干杯!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-04
    • 2011-07-23
    相关资源
    最近更新 更多