1

I'm trying to work with a texture atlas and the canvas tag to do some animation. I don't get any errors but all I am seeing is the last frame. Is there something I should be doing so I see all the "frames"?

I have tested this with hard coding the x/y coordinates on the texture atlas so I know I can cruise around it.

Here's my code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html lang="en">
<head>
<meta charset="utf-8">
<title>Canvas Animation</title>
<!-- meta tags -->
<meta name="keywords" content="">
<meta name="description" content="">

<!-- javascript -->

<script language="javascript">

var textureAtlas = new Image();
var moeImg = new Image();

function init() { 

    animateProps = new Array;

    textureAtlas.src = "images/textureatlast1.png";
    moeImg.src = "images/moe.jpg";
    var textureAtlasCoords = new Array("0,0", "100,0", "200,0", "300,0", "400,0", "500,0", "600,0");

        for(var c=0; c<textureAtlasCoords.length; c++) {

            animateObj = new Object();

            var thisCoord = textureAtlasCoords[c];
            var thisCoordSplit = thisCoord.split(",");
            var thisX = thisCoordSplit[0];
            var thisY = thisCoordSplit[1]; 

            //var a = setTimeout(Function(){animate(ctx, textureAtlas, thisX, thisY)},1000);
            animateObj['canvasId'] = "princessAnimation"; 
            animateObj['imgsrc'] = textureAtlas;
            animateObj['x'] = thisX;
            animateObj['y'] = thisY;

            animateProps.push(animateObj);

            var a = setInterval(function(){animate(animateProps);},1000);

        }

        clearInterval(a);

}


function imgDraw(ctx, thisImg) { 

    console.log(thisImg);
    //(image, x(
    ctx.drawImage(thisImg,0,0, 1024, 451, 0, 0, 1024, 451);

}

function animate() { 

        //get animation properties
        for(thisProp in animateProps) {
            var canvasId = animateProps[thisProp]['canvasId']; 
            var thisImg = animateProps[thisProp]['imgsrc'];
            var thisX = animateProps[thisProp]['x'];
            var thisY = animateProps[thisProp]['y'];

        }

        var canvas = document.getElementById(canvasId);
    if (canvas.getContext){

        var ctx = canvas.getContext('2d');
        ctx.clearRect(0,0,1024,451);
        ctx.drawImage(thisImg,thisX,thisY, 1024, 451, 0, 0, 1024, 451);
    }

}
</script>

<!-- stylesheets -->

<!--conditional comments -->

</head>

<body onload="init();">


<div id="animationWrapper">
    <canvas width="100" height="150" id="princessAnimation"></canvas>
</div>

</body>
</html>

Here's the image I am working with (Note: I know my coordinates are off per the file, right now I am just trying to get the transition to work, I'll then fine tune the x/y coordinates (of course unless you want to do that for me. : D )

enter image description here

PruitIgoe
  • 6,166
  • 16
  • 70
  • 137
  • I'm not sure how you expect this to work at all. Some corrections: 1) `animateProps = [];` 2) you can't use for(_ in array) on a JavaScript array 3) `animateObj = {};` 4) `function animate(animateProps){` 5) use an HTML 5 doctype – dtanders Aug 09 '11 at 18:21
  • 2: Well it works for me, copy my code and console.log(animateProps[thisProp]['canvasId']); and you'll get princessAnimation, which is what I am passing. Since I am storing an object as the first item in the array I am assuming it is working like this array[0]['princessAnimation'] where item 0 of the array is the object. – PruitIgoe Aug 09 '11 at 18:38
  • 3: I have used this before - http://stackoverflow.com/questions/684575/how-to-quickly-clear-a-javascript-object - it's probably leading to some garbage issues but it works... – PruitIgoe Aug 09 '11 at 18:39
  • 4: ? Not sure what you're trying to say here. If you're questioning the setInterval you cannot pass vars to a function in a setInterval, you can add them as a 3rd parameter but that leads to IE issues, using a closure resolves that - http://www.makemineatriple.com/2007/10/passing-parameters-to-a-function-called-with-settimeout – PruitIgoe Aug 09 '11 at 18:41
  • 5: Got me on that one, but in my defense I am hacking at this for fun and learning and just grabbed a clip from my CODA snippets without looking... – PruitIgoe Aug 09 '11 at 18:42
  • 1: Forgot that - ? again, not sure of your critique. – PruitIgoe Aug 09 '11 at 18:43
  • 1) http://bonsaiden.github.com/JavaScript-Garden/#array.constructor 2) is probably just my antiquated knowledge - I swear IE used to enumerate the length property 4) I didn't see that you had made `animateProps` global (which is also bad practice) or it wouldn't work as far as I can see – dtanders Aug 09 '11 at 19:46
  • 4.) Ideally, you wouldn't even do this – you'd call animate, and use variables in scope to carry the information. By encapsulating this into a class you'd avoid that problem. When it's for learning purposes or proof of concept (non-production), finger-wagging about good/bad practices doesn't help anything. – stslavik Aug 09 '11 at 19:52
  • @dtanders - nice link. That's some pretty good stuff. – PruitIgoe Aug 10 '11 at 13:43

1 Answers1

0

It's the interval. You're setting them all before animate is ever fired, overwriting the previous interval. So you just keep printing out the 0,600 coordinate. It's not the only thing wrong from what I can see, but it's the reason you're only getting the last image.

Push each coordinate, then use the interval to loop through the animations. Don't set the interval until all of them are pushed, then use a global to count the steps, increment on animation (upon redrawing to the canvas).

For example (simplified, and this may need some work to get working the way you want):

var textureAtlas = new Image(),
    steps = 0, maxsteps = 0;
//var moeImg = new Image();
var a;


function init() { 

  animateProps = new Array;

  textureAtlas.src = "textureatlas1.png";
  //moeImg.src = "images/moe.jpg";
  var textureAtlasCoords = new Array("0,0", "100,0", "200,0", "300,0", "400,0", "500,0", "600,0");
  maxsteps = textureAtlasCoords.length;
    for(var c=0; c<textureAtlasCoords.length; c++) {

      animateObj = new Object();

      var thisCoord = textureAtlasCoords[c];
      var thisCoordSplit = thisCoord.split(",");
      var thisX = thisCoordSplit[0];
      var thisY = thisCoordSplit[1]; 

      //var a = setTimeout(Function(){animate(ctx, textureAtlas, thisX, thisY)},1000);
      animateObj['canvasId'] = "princessAnimation"; 
      animateObj['imgsrc'] = textureAtlas;
      animateObj['x'] = thisX;
      animateObj['y'] = thisY;

      animateProps.push(animateObj);
    }
    a = setInterval(function(){animate(animateProps);},1000);
}
function animate() { 
    if(steps>=maxsteps) steps =0;
    //get animation properties

      var canvasId = animateProps[steps]['canvasId']; 
      var thisImg = animateProps[setps]['imgsrc'];
      var thisX = animateProps[steps]['x'];
      var thisY = animateProps[steps]['y'];

    var canvas = document.getElementById(canvasId);
  if (canvas.getContext){

    var ctx = canvas.getContext('2d');
    ctx.clearRect(0,0,1024,451);
    console.log(thisX+" "+thisY);
    ctx.drawImage(thisImg,thisX,thisY, 1024, 451, 0, 0, 1024, 451);
  }
  steps++;
}

I'm also not so sure on ctx.drawImage(thisImg,thisX,thisY, 1024, 451, 0, 0, 1024, 451);... I feel like you're not binding the image to the right parameters. Remember that ctx.drawImage is img, sx, sy, sw, sh, dx, dy, dw, dh for the args. img - the image. sx, sy, sw, sh - clip to the rectangle defined by this. dw, dh - scale to these dimensions dx, dy - position here.

Hope that helps some.

stslavik
  • 2,920
  • 2
  • 19
  • 31
  • Thanks for the help, it makes sense except how do I loop the x number of animation frames into animate(), in the code above won't setInterval only fire once? – PruitIgoe Aug 09 '11 at 18:54
  • setInterval is window based (`window.setInterval(fn, interval)`), and continues to fire until the interval is cleared (`clearInterval(a)`). – stslavik Aug 09 '11 at 18:58
  • Hah! Sorry I missed that. You're right - there was a scope issue on the variable. You'd set it but never be able to clear the interval. Made the var (a) global in case you need to clear it later. – stslavik Aug 09 '11 at 19:45