【问题标题】:How to reset canvas context state entirely如何完全重置画布上下文状态
【发布时间】:2015-02-07 00:00:08
【问题描述】:

我有一个简单的画布动画,如下:

function animate(){

                var canvas = document.getElementById("canvas");

                canvas.width = $(window).width();


                canvas.addEventListener("click", replay, false);
                var context = canvas.getContext("2d");


               //animation code

               function replay(e){

                   animate();

                 }

}

所以我的期望是当用户单击画布时动画将重播,因为我正在重新分配宽度:
canvas.width = $(window).width();

这将重置整个上下文状态(在此处阅读http://diveintohtml5.info/canvas.html

它在您第一次单击画布时起作用,但之后它会记住上下文转换状态并显示一些奇怪的动画。

我尝试添加:
context.setTransform(1, 0, 0, 1, 0, 0);

在重新绘制之前将转换矩阵显式重置为单位矩阵,但没有效果。

和这个How to clear the canvas for redrawing不一样

因为它是关于如何清除 display 的上下文,但我想清除 everything display 加上 context 状态

我尝试在动画期间记录每个变量的状态,但 ff 代码有些奇怪。

  function moveHorizontal(x){

    if(x < 100 ){
                            context.translate(1, 0);
                            console.log("x:"+x);
                            drawImage();
                            x += 1;
                            setTimeout(moveHorizontal,10,x);

    }    
}

我最初将此函数称为 moveHorizo​​ntal(0)

所以日志第一次符合预期:

x:0
x:1
x:2
.
.
x:99

当我点击“replay”时,我得到与上面相同的内容:

  x:0
  x:1
  x:2
  .
  .
  x:99

这是正确,但是当我第二次点击“重播”时 我得到了这个:

  x:0
  x:0     
  x:1
  x:1
  .
  .
  .      
  x:99
  x:99

这会使翻译增加三倍,导致意想不到的效果。

有什么解释吗?

【问题讨论】:

标签: html canvas


【解决方案1】:

更新

有了新信息,这可能是问题的原因(没有足够的代码来 100% 确定,但它应该可以很好地指出问题所在):

由于代码是异步的,并且似乎共享一个全局/父变量x,因此会发生这种情况,当在某个时间点,在前一个停止之前调用重放时,x 被重置为 0,从而导致两者 (或 n 个循环)继续。

为了防止这种情况,您需要为每次运行隔离 x 变量,或者使用更简单的方法阻止循环运行(如果它已经在使用先前调用中的 x 运行)。您可以使用标志来执行此操作:

var x = 0,
    isBusy = false;

function animation(x) {

    if (isBusy) return;

    if (x < 100) {
        isBusy = true;

        // ...
        setTimeout(...); // etc.

    }
    else {
        isBusy = false;
    }

}

(可选)存储来自 setTimeout (reqID = setTimeout(...)) 的超时 ID,并在调用新循环时使用 clearTimeout(reqID)

旧答案

完全重置上下文状态的唯一方法是:

  1. 设置新维度
  2. 用新画布替换它(从技术上讲,它不会重置)

后者应该是不必要的。

根据standard

当用户代理要将位图尺寸设置为宽度和高度时, 它必须运行以下步骤:

  1. 将呈现上下文重置为其默认状态。
    ...

在您帖子的示例代码中,您使用的是 jQuery 和 vanilla JavaScript 的混合。使用画布(或视频元素)时,我始终建议仅使用原生 JavaScript。

由于您没有显示更多代码,因此我们无法查明任何具体错误。但由于设置单位矩阵和设置画布大小似乎都不起作用,这表明问题出在代码的不同部分。也许您的对象涉及未重置的持久偏移。

尝试消除使用 jQuery 设置宽度时可能出现的任何错误:

canvas.width = window.innerWidth; // and the same for height

我注意到的另外两件事是,您在似乎是循环的一部分的函数内设置了画布的大小 - 如果是这种情况,应该避免。本质上,每次发生这种情况时,浏览器都必须构建一个新的画布。二、这一行:

canvas.addEventListener("click", replay, false);

可以简单地替换为(基于所示代码):

canvas.addEventListener("click", animate, false);

不过,您需要发布更多代码才能获得更深入的答案。但作为关于上下文的结论:设置新大小重置它。问题可能出在代码的其他地方。

【讨论】:

  • 谢谢,我将删除 jquery 代码并使用 vanilla JavaScrip。如果它对我有用,我会接受你的回答。
  • @yola21 请注意,问题可能出在代码的其他部分。我用参考更新了答案。
  • 你是对的,问题出在代码的其他部分。我更新了我的问题。这只是奇怪的错误。
  • @yola21 更新了 似乎 问题的答案,但没有足够的代码达到 100%。不过,它应该给出一个很好的指示。希望这会有所帮助!
  • 非常感谢第二个帮助了我;清除超时调用。我收集了动画的所有 reqID 并在开始第二个动画之前清除了它们。 完美运行
猜你喜欢
  • 2012-12-25
  • 2016-02-15
  • 2020-04-18
  • 2016-08-26
  • 2012-05-22
  • 1970-01-01
  • 2016-12-25
  • 2021-07-25
相关资源
最近更新 更多