1

I have a simple canvas animation, as follows:

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

                 }

}

So my expectation is when the user clicks the canvas the animation will replay because I am reassigning the width:
canvas.width = $(window).width();

which will reset the entire context state ( read it here http://diveintohtml5.info/canvas.html)

It works the first time you click the canvas but after that it remembers the context transformation state and shows some weird animation.

I tried adding this:
context.setTransform( 1, 0, 0, 1, 0, 0 );

to explicitly reset the transformation matrix to identity matrix before re-drawing but has no effect.

It is different from this How to clear the canvas for redrawing

because it is about how to clear the display of context but I want to clear everything the display plus the context state.

I tried logging the state of each variable during the animation I get something weird with the ff code.

  function moveHorizontal(x){

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

    }    
}

I am calling this function initially as moveHorizontal(0).

so the log is as expected the first time:

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

when I click "replay" I get same thing as above:

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

which is correct but when I click "replay" the second time I am getting this:

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

which triples the translation leading to unexpected effect.

any explanation?

Community
  • 1
  • 1
t31321
  • 673
  • 1
  • 6
  • 20

1 Answers1

1

Update

With the new information this may be the cause of the problem (there is not enough code to be 100% sure but it should give a good pointer as to where the problem is):

As the code is asynchronous and seem to share a global/parent variable x, what happens is that when, at some point, the replay is invoked before one earlier has stopped, the x is reset to 0 causing both (or n number of loops) to continue.

To prevent this you either need to isolate the x variable for each run, or using a simpler approach blocking the loop from running if it's already running using x from a previous invocation. You can use a flag to do this:

var x = 0,
    isBusy = false;

function animation(x) {

    if (isBusy) return;

    if (x < 100) {
        isBusy = true;

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

    }
    else {
        isBusy = false;
    }

}

optionally, store the timeout ID from setTimeout (reqID = setTimeout(...)) and use clearTimeout(reqID) when you invoke a new loop.

Old answer

The only way to reset the context state entirely is to:

  1. Set a new dimension
  2. Replace it with a new canvas (which technically isn't resetting)

The latter should be unnecessary though.

According to the standard:

When the user agent is to set bitmap dimensions to width and height, it must run the following steps:

  1. Reset the rendering context to its default state.
    ...

In the example code in your post you are using a mix of jQuery and vanilla JavaScript. When working with canvas (or video element) I always recommend using vanilla JavaScript only.

Since you are not showing more of the code it is not possible for us to pinpoint to any specific error. But as neither setting an identity matrix nor setting a size for canvas seem to work, it indicates that the problem is in a different part of the code. Perhaps you have persistent offsets involved for your objects that are not reset.

Try to eliminate any possible errors coming from using jQuery to set width as well:

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

Two other things I notice is that you set the size of canvas inside a function that appears to be part of a loop - if this is the case it should be avoided. The browser will have to, in essence, build a new canvas every time this happens. Second, this line:

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

can simply be replaced with (based on the code as shown):

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

You will need to post more code to get a deeper going answer though. But as a conclusion in regards to context: setting a new size will reset it. The problem is likely somewhere else in the code.

  • Thanks I will remove jquery codes and use vanilla JavaScrip. I will accept your answer if it works for me. – t31321 Dec 09 '14 at 12:16
  • @yola21 just note that the problem is likely to other parts of the code. I updated the answer with a reference. –  Dec 09 '14 at 12:17
  • you are right, the problem was on some other part of the code. I hv updated my question. It is just weird error. – t31321 Dec 09 '14 at 14:25
  • @yola21 updated answer with what *seem* to be the problem, but there's not enough code to be 100%. It should give a good pointer though. Hope this helps! –  Dec 09 '14 at 16:31
  • the global variable is is primitive type which is a number, so calling moveHorizontal(primitive_type_variable) is pass by value rather than pass by reference. so obviously not sharing global variable. – t31321 Dec 10 '14 at 06:20
  • 1
    Thanks a lot the __second one__ helped me;clearing the timeout call. I collected all **reqID** of my animation and cleared them before beginning the second one. __It works perfect__. – t31321 Dec 10 '14 at 06:39