Last year I had blogged about animating sprite using Kinetic JS.
Code in that post was part of a simple game I had created. So the code specific for animating sprite was explained in snippets. A reader of that post contacted me with a request to provide a complete example. So I created a small demo of sprite animation only. If you are interested, you can download it from here. This demo animates images in the sprite sheet at a fixed location, it does not move the image. So I thought it would be interesting to add motion to sprite images when they are animated.
First, take a look at the demo. Click on the Walk button to make the person walk. You can stop any time. If the person hits right side wall, then she falls, which is also simulated using sprite animation. I know the ‘walk’ does not look natural, but creating graphics is not my string point and this is the best I could manage.
The above example contains two instances of sprite animation. One for walking and another to simulate fall. I used a single sprite sheet to contain images required for both these animations.
The first four images are used for walking action and the next four are used to simulate fall action. I create a single JavaScript object with two animation keys (walk and fall).
Kinetic.Sprite object has a method afterFrame. You can specify a callback function to be invoked after certain number of frames are displayed. I thought I would use this function to increment X position of images, to move them along the X axis. However I found that this function is called only once. Looking at the source code of KineticJS confirmed this. In fact the callback function is removed from the object after invoking it once.
I then ended up using setInterval function to increment the X position. However I had to make sure that interval is set equal to what KineticJS uses to change frames. Sprite object has an attribute called frameRate, which is the number of frames displayed per second. So the value of interval I set (in setInterval function) is 1000 / frameRate. This values is specified in milli seconds, hence use of 1000.
You can download the complete demo code and images from here. And here is the source code, if you just want to browse it.
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="kinetic-v4.4.3.min.js"></script> <script type="text/JavaScript"> var initX = 5, initY = 5, //Width and height of Kinetic Stage stageWidth = 500, stageHeight = 115, currX = initX, frameRate = 5, averageSpriteWidth = 42, averageFallSpriteWidth = 100, walking = false; //Positions of images in sprite walkingPersonAnimationInfo = { walk :[ { x:4, y:3, width:42, height:100 }, { x:66, y:3, width:42, height:100 }, { x:128, y:3, width:42, height:100 }, { x:197, y:3, width:42, height:100 } ], fall :[ { x:258, y:3, width:42, height:100 }, { x:314, y:3, width:68, height:100 }, { x:389, y:3, width:93, height:100 }, { x:500, y:3, width:99, height:100 } ] }; function onBodyLoad() { //Create Kinetic Stage stage = new Kinetic.Stage({ container: "walkingStage", width:stageWidth, height:stageHeight }); layer = new Kinetic.Layer(); //create a border var border = new Kinetic.Rect({ x:0, y:0, width: stage.getWidth()-2, height: stage.getHeight()-2, stroke:'black', strokeWidth:1 }); layer.add(border); loadStandingPersonImage(); loadFallenPersonImage(); loadSpriteImage(); //Add layer to the stage stage.add(layer); } function loadStandingPersonImage() { //Load Standing Person image standingImg = new Image(); standingImg.onload = function (){ kjsStandingImg = new Kinetic.Image({ x:initX, y:initY, image:standingImg }); layer.add(kjsStandingImg); layer.draw(); } standingImg.src = "standing_person.png"; } function loadFallenPersonImage() { //Load Fallen Person image fallenImg = new Image(); fallenImg.onload = function (){ kjsFallenImg = new Kinetic.Image({ x:initX, y:initY, image:fallenImg }); kjsFallenImg.hide(); layer.add(kjsFallenImg); } fallenImg.src = "fallen_person.png"; } function loadSpriteImage() { //Load sprite image spriteImg = new Image(); spriteImg.onload= function() { kjsSprite = new Kinetic.Sprite({ x:0,y:0, image:spriteImg, animation:"walk", animations:walkingPersonAnimationInfo, frameRate:frameRate }); kjsSprite.hide(); layer.add(kjsSprite); } spriteImg.src = "walking_person_sprite_sheet.png"; } function onWalkBtnClick() { var btnElement = document.getElementById("walkBtn"), mode = btnElement.getAttribute("value"); if (mode == "Walk") { mode = "Stop"; startWalking(); } else { mode = "Walk"; stopWalking(); } document.getElementById("walkBtn").setAttribute("value",mode); } //Starts sprite animation function startWalking() { var speedStr = document.getElementById("speedTxt").value; var speed = Number(speedStr); if (isNaN(speed) || speed > 10 || speed < 1) { speed = 5; document.getElementById("speedTxt").value = speed; } frameRate = speed; kjsFallenImg.hide(); kjsStandingImg.hide(); kjsSprite.setAnimation("walk"); kjsSprite.setFrameRate(frameRate); //Move sprite image to the location of standing person image currX = kjsStandingImg.getX(); kjsSprite.setX(currX); kjsSprite.setY(kjsStandingImg.getY()); kjsSprite.show(); var stageWidth = stage.getWidth(), stopWalking = false, walkInterval = setInterval(function(){ if (!walking) { clearInterval(walkInterval); return; } currX += 10; if (currX + averageSpriteWidth > stageWidth) { if (currX < stageWidth && !stopWalking) { currX = stageWidth - averageSpriteWidth; stopWalking = true; } else { clearInterval(walkInterval); startFalling(); return; } } kjsSprite.setX(currX); }, 1000 / frameRate); kjsSprite.start(); walking = true; } //Starting falling animation function startFalling() { walking = false; kjsSprite.setAnimation("fall"); kjsSprite.setX(stage.getWidth()-averageFallSpriteWidth); kjsSprite.afterFrame(3,function(){ kjsSprite.stop(); kjsFallenImg.setX(stage.getWidth()-kjsFallenImg.getWidth()-4); kjsFallenImg.setY(stage.getHeight()-kjsFallenImg.getHeight()-4); kjsSprite.hide(); kjsFallenImg.show(); layer.draw(); document.getElementById("walkBtn").setAttribute("value","Walk"); }); } function stopWalking() { walking = false; kjsSprite.stop(); kjsSprite.hide(); kjsFallenImg.hide(); kjsStandingImg.show(); layer.draw(); document.getElementById("walkBtn").setAttribute("value","Walk"); } </script> </head> <bodyonload="onBodyLoad()"> <divid="walkingStage"></div> <br> <div> Walking Speed : <inputtype="text"id="speedTxt"value="5"size="2"/> (Enter value from 1 to 10)<br> <inputtype="button"id="walkBtn"value="Walk"onclick="onWalkBtnClick()"/> </div> </body> </html>
-Ram Kulkarni
Dear Ram,
I downloaded Small demo of sprite animation and It’s not working properly in my System.
Sorry, there was an error in the code. I have fixed it. Please try again.