How to animate sprites on keypress on canvas? - javascript

How do I animate the character on keypress? I changed the sprite position which shows the next image, but how do I loop through the two images so that I can show the player running while the key is pressed.
I need the first and the second frame.
Keyevents:
if(keys[39]){
//right arrow
if (mario.velX < mario.speed){
mario.velX++;
if(!mario.jumping){
//mario sprite position
mario.frame = 0;
}
}
}
And the draw function
this.frame = 0;
var marioImg; //mario image
var that = this;
this.init = function() {
marioSprite = new Image();
marioSprite.src = 'images/mario-sprites.png';
}
this.draw = function(){
that.sX = that.width * that.frame;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(marioSprite, that.sX, that.sY, that.width, that.height, that.x, that.y, that.width, that.height);
}

Load the two images into and array
var imageArray = []; // array to hold images
var img = new Image(); // create and load first image
img.src = "imageOne.png";
imageArray.push(img); // put it in the array
img = new Image(); // same for image two
img.src = "imageTwo.png";
imageArray.push(img);
You will need some variables. One to control how long each image is displayed and another to hold which image is displayed. You can use the current time to keep it nice and even.
var millsecondsPerImage = 100; // each frame is 100 ms 1/10th of a second
var currentTime = new Date().valueOf(); // get the time in milliseconds
// Divide current time by how long to display for. Round down with floor
// then modulo the length of the image array
var imageToDraw = imageArray[Math.floor(currentTime / millsecondsPerImage) % imageArraylength];
// draw the current image image
ctx.drawImage(imageToDraw, posx, posy);
That will cycle any number of images, how ever many you put in the array.

Related

Images not displaying the first time in this object program in JS

I am making a battleship game with polar coordinates. After the user chooses two points, a battleship should be drawn in the middle. My Battleship constructor looks like this:
function Battleship(size, location, source){
this.size = size;
//initializing the image
this.image = new Image();
this.image.src = source;
this.getMiddlePoint = function(){
//get midpoint of ship
...
}
this.distanceBetween = function(t1, t2){
//dist between two points
}
this.display = function(){
var point = [this.radius];
point.push(this.getMiddlePoint());
point = polarToReal(point[0], point[1] * Math.PI / 12);
//now point has canvas coordinates of midpoint
var width = this.distanceBetween(this.info[0][0], this.info[this.info.length-1][0]);
var ratio = this.image.width / width;
ctx.drawImage(this.image, point[0] - width/2, point[1] - this.image.height / ratio / 2, width, this.image.height / ratio);
//draws the image
}
}
The display method of each ship gets called at a certain point (after the user has chosen the location). For some reason, the images do not show the first time I do this, but when I run this code at the very end:
for(var i = 0; i<playerMap.ships.length; i++){
playerMap.ships[i].display();
}
All ships are displayed correctly (not aligned well, but they are displayed). I think there is a problem with loading the images. I am not sure how to fix this. I tried using image.onload but I never got that to work. I also tried something like this:
var loadImage = function (url, ctx) {
var img = new Image();
img.src = url
img.onload = function () {
ctx.drawImage(img, 0, 0);
}
}
but the same problem kept happening. Please help me fix this problem. Here is the game in its current condition. If you place ships, nothing happens, but after you place 5 (or 10) ships, they suddenly all load.
EDIT:
I solved the problem by globally defining the images. This is still very bad practice, since I wanted this to be in the battleship object. This is my (temporary) solution:
var sub = [];
for(var i = 1; i<5; i++){
sub[i] = new Image();
sub[i].src = "/img/ships/battleship_"+i+".png";
}

combine array of images with javascript (with or without canvas)

I would like to create a strip of images and compose a new image, like image = [image0-image1-image2].
We'll use:
images = ['https://upload.wikimedia.org/wikipedia/commons/5/55/Al-Farabi.jpg',
'https://upload.wikimedia.org/wikipedia/commons/e/e1/FullMoon2010.jpg',
'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/3D_coordinate_system.svg/10000px-3D_coordinate_system.svg.png']
I would like to take external above, and make a collage.
I would like to do it in background.
I learnt that is possible to use a canvas element off the dom; for the sake of watching what I am doing, I will use a canvas element here.
// create an off-screen canvas using document.createElement('canvas')
// here I use a canvas in DOM cause I cannot find a way to displayed the final collage
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
// set its dimension to target size
canvas.width = 1200;
canvas.height = 630;
and found three different behaviors for what I think should give same result. Could you explain me why?
If I manually copy and paste in console code for each image, one at a timeenter code here`
var image = new Image();
// i is the i-th element in images
image.src = images[i];
image.onload = function() {
context.save();
context.drawImage(image, canvas.width * 0.3 * i, 0, canvas.width*0.3, canvas.height);
}
I can see the elements are positioned one aside of the other, like I would like to have.
But If I copy all of three pieces of code at once, either in a loop, I can see only the last image placed in all of the three different positions:
for (var i = images.length; i <= 0; i++) {
var image = new Image();
image.src = images[i];
image.onload = function(){
context.save();
context.drawImage(image, canvas.width*0.3 * i, 0, canvas.width*0.3, canvas.height);
}
}
So I thought, maybe it's a matter of using a callback after image is loaded - I tried the following but nothing happens: canvas stays empty.
// my callback
function addImage(image, position){
image.onload = function(){
context.save();
context.drawImage(image, canvas.width*0.3 * position, 0, canvas.width*0.3, canvas.height);
}
}
function loadImages (images, callback) {
for (var i = images.length-1; i >= 0; i--) {
var image = new Image();
image.src = images[i];
callback(image, i);
}
}
// canvas will stay empty:
loadImages(images, addImage);
Can you help in clarifying the differences in the three parts, and figure out how to combine an array of images in a single one?
Possibly in background, I want to then save the image and post it via ajax.
In your loop example, all the onload functions are sharing the same i and image variables from the loop. But the onload functions are callback functions that get called after the loop completes. Thus, all the onload functions are using the same i and image values from after the loop completed. You need to create a local scope such that each onload function has its own i and image values. For example...
for (var i = 0; i < images.length; i++) {
var image = new Image();
image.src = images[i];
image.onload = function(image, i) {
return function(){
context.drawImage(image, canvas.width*0.3 * i, 0, canvas.width*0.3, canvas.height);
}
}(image, i);
}

Can't load images on canvas for my js game

I am making a mahjong game in js and I have a problem loading images on canvas
canvas
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 512;
//draw canvas
document.body.appendChild(canvas);
Arrays
//Array with Tiles & set length to 144 which are the requested tiles
var tiles = new Array(2);//will be 144
//2D map array for showing the images coordinates
var map = [[69,50],[100,150]];//will be 144 coordinates
my function update()
function update(){
for(var i=0;i<tiles.length;i++){
//make the tile object
tiles[i] = new Object();
tiles[i].id = i ;
tiles[i].selected = false;
//set the coordinates from map Array
tiles[i].x=map[i][0];
tiles[i].y=map[i][1];
//tiles[i].ready=false;
//These are for the image location works fine
//convert i to String
var sourceNumber = i.toString();
//add .png to String
var source = sourceNumber.concat(png);
//add /image
var source = dest.concat(source);
tiles[i].img = new Image();
tiles[i].img.onload = function(){
//tiles[i].ready=true;
ctx.drawImage(tiles[i].img,tiles[i].x,tiles[i].y,xdimension,ydimension);
};
tiles[i].img.src = source ;
}
}
I runned it on each of my browser it won't load images , I debugged on chrome and it says on ctx.drawImage(...); -> Uncaught TypeError: Cannot read property 'img' of undefined(repeated many times), So I tried the tiles[I].ready and after load images but still has that error.Any suggestions on how should I implement the loading of the tile images
The first time ctx.drawImage is called, the value for i is 2. The problem is that the for-loop (for(var i=0;i<tiles.length;i++)) has finished executing before any of the images have loaded. Consequently, the value of i at the time the onload function is called is the value at which the loop ceased being run. The easiest way around this is to save the index (i) into the img element itself, so that you can retrieve it in the onload handler.
Here's a simple adaption of your code that seems to work just fine.
The important changes are:
tiles[i].img.iVal = i;
and the body of the onload handler.
I also:
(a) added an array to hold hard-coded image names for convenience, rather than dynamically creating them (I'd have had to name some images into the format that the code computes)
(b) removed the xdimension and ydimension vars from the drawImage call since I dont know what they are.
(c) changed .concat(png) to .concat(".png") since it was easier than declaring a variable called png that holds the string .png
Anyway, here's the sample-code I used:
<!DOCTYPE html>
<html>
<head>
<script>
window.addEventListener('load', onDocLoaded, false);
var canvas, ctx, tiles, map;
function onDocLoaded()
{
canvas = newEl('canvas');
ctx = canvas.getContext('2d');
canvas.width = canvas.height = 512;
document.body.appendChild(canvas);
tiles = new Array(2);
map = [[69,50],[100,150]];
update();
}
var imgFileNames = ["img/girl.png", "img/redbaron.png"];
function update()
{
for(var i=0;i<tiles.length;i++)
{
//make the tile object
tiles[i] = new Object();
tiles[i].id = i ;
tiles[i].selected = false;
//set the coordinates from map Array
tiles[i].x=map[i][0];
tiles[i].y=map[i][1];
//tiles[i].ready=false;
//These are for the image location works fine
//convert i to String
// var sourceNumber = i.toString();
//add .png to String
// var source = sourceNumber.concat(".png");
//add /image
// var source = dest.concat(source);
tiles[i].img = new Image();
tiles[i].img.iVal = i;
tiles[i].img.onload =
function()
{
var curI = this.iVal;
ctx.drawImage(tiles[curI].img,tiles[curI].x,tiles[curI].y);
// ctx.drawImage(this,tiles[curI].x,tiles[curI].y); //equiv to above line
};
tiles[i].img.src = imgFileNames[i];
}
}
</script>
<style>
canvas
{
border: solid 1px red;
}
</style>
</head>
<body>
</body>
</html>

How do I handle many images in my HTML5 canvas?

I'm very new to Html5 canvas and Javascript. I'm trying this :
function animate() {
var image1 = new Image();
image.src = /path
var image2 = new Image();
image2.src = /path
for(;;)
{
//change value of x and y so that it looks like moving
context.beginPath();
context.drawImage(<image>, x, y );
context.closePath();
context.fill();
}
}
EDIT:
And I call the animate function each 33ms :
if (playAnimation) {
// Run the animation loop again in 33 milliseconds
setTimeout(animate, 33);
};
If I follow the answer given here, I get the image struck and its not moving any further.
Update: Based on new information in the question, your problem (restated) is that you want to either
wait for all images to load first, and then start animating with them, or
start animating and only use an image if it is available.
Both are described below.
1. Loading many images and proceeding only when they are finished
With this technique we load all images immediately and when the last has loaded we run a custom callback.
Demo: http://jsfiddle.net/3MPrT/1/
// Load images and run the whenLoaded callback when all have loaded;
// The callback is passed an array of loaded Image objects.
function loadImages(paths,whenLoaded){
var imgs=[];
paths.forEach(function(path){
var img = new Image;
img.onload = function(){
imgs.push(img);
if (imgs.length==paths.length) whenLoaded(imgs);
}
img.src = path;
});
}
var imagePaths = [...]; // array of strings
loadImages(imagePaths,function(loadedImages){
setInterval(function(){ animateInCircle(loadedImages) }, 30);
});
2. Keeping track of all images loaded so far
With this technique we start animating immediately, but only draw images once they are loaded. Our circle dynamically changes dimension based on how many images are loaded so far.
Demo: http://jsfiddle.net/3MPrT/2/
var imagePaths = [...]; // array of strings
var loadedImages = []; // array of Image objects loaded so far
imagePaths.forEach(function(path){
// When an image has loaded, add it to the array of loaded images
var img = new Image;
img.onload = function(){ loadedImages.push(img); }
img.src = path;
});
setInterval(function(){
// Only animate the images loaded so far
animateInCircle(loadedImages);
}, 100);
And, if you wanted the images to rotate in a circle instead of just move in a circle:
Rotating images: http://jsfiddle.net/3MPrT/7/
ctx.save();
ctx.translate(cx,cy); // Center of circle
ctx.rotate( (angleOffset+(new Date)/3000) % Math.TAU );
ctx.translate(radius-img.width/2,-img.height/2);
ctx.drawImage(img,0,0);
ctx.restore();
Original answer follows.
In general, you must wait for each image loading to complete:
function animate(){
var img1 = new Image;
img1.onload = function(){
context.drawImage(img1,x1,y1);
};
img1.src = "/path";
var img2 = new Image;
img2.onload = function(){
context.drawImage(img2,x2,y2);
};
img2.src = "/path";
}
You may want to make this code more DRY by using an object:
var imgLocs = {
"/path1" : { x:17, y:42 },
"/path2" : { x:99, y:131 },
// as many as you want
};
function animate(){
for (var path in imgLocs){
(function(imgPath){
var xy = imgLocs[imgPath];
var img = new Image;
img.onload = function(){
context.drawImage( img, xy.x, xy.y );
}
img.src = imgPath;
})(path);
}
}

JavaScript iteration

I want to use JavaScript to draw a series of images onto an HTML5 canvas. I have the following while loop, which I had hoped would draw all of the images to the canvas, however, it is currently only drawing the first one:
function drawLevelOneElements(){
/*First, clear the canvas */
context.clearRect(0, 0, myGameCanvas.width, myGameCanvas.height);
/*This line clears all of the elements that were previously drawn on the canvas. */
/*Then redraw the game elements */
drawGameElements();
/*Draw the elements needed for level 1 (26/04/2012) */
var fileName = 1;
var imagePositionX = 20;
var imagePositionY = 30;
while(fileName < 11){
/*Create an array of images here, move to next element of array on each iteration */
var numbers = new Array();
numbers[0] = "1.png"
numbers[1] = "2.png"
numbers[3] = "3.png"
numbers[4] = "4.png"
numbers[5] = "5.png"
image.src = fileName+".png";
image.src = numbers[0];
image.onload = function(){
context.drawImage(image, imagePositionX, imagePositionY, 50, 50);
}
fileName = fileName+1;
imageY = imageY+20;
console.dir(fileName); /* displays in the console- helpful for debugging */
}
To talk through what I had hoped this function would do:
Load each of the images into a different element of the array (so 1.png would be in numbers[0], 2.png in numbers[1], etc. )
It would then take the global variable 'image', and assign its source to the contents of numbers[0]
Then draw that image at the specified position on the canvas.
Then increment the value of the variable fileName by 1, giving it a value of '2'
Next it would increment the value of the Y co-ordinate where it will draw the image on the canvas by 20- moving the position of the image to be drawn down by 20 pixels
After that it would go back to the start of the loop and draw the next image (2.png) on the canvas in a position that is 20 pixels below the position of the first image that was drawn.
It should continue doing this while the value of the variable 'fileName' is less than 11, i.e. it should draw 10 images each new one below the last one that was drawn.
However, for some reason, my function only draws the first image. Could someone point out what I'm doing wrong, and how I could correct this?
Thanks very much.
Edited and commented some points of your code.
The most effective change was at imageY = imageY+20; that was edited to use imagePositionY variable.
function drawLevelOneElements() {
/*First, clear the canvas */
context.clearRect(0, 0, myGameCanvas.width, myGameCanvas.height);
/*This line clears all of the elements that were previously drawn on the canvas. */
/*Then redraw the game elements */
drawGameElements();
/*Draw the elements needed for level 1 (26/04/2012) */
var fileName = 1;
var imagePositionX = 20;
var imagePositionY = 30;
while(fileName < 11){
/*Create an array of images here, move to next element of array on each iteration */
var numbers = new Array();
/* what is not used is not necessary :)
numbers[0] = "1.png"
numbers[1] = "2.png"
numbers[3] = "3.png"
numbers[4] = "4.png"
numbers[5] = "5.png"*/
image.src = fileName+".png";
// image.src = numbers[0];
image.onload = function(){
context.drawImage(image, imagePositionX, imagePositionY, 50, 50);
}
fileName = fileName+1;
imagePositionY = imagePositionY+20; //before: imageY = imageY+20;
console.dir(fileName); /* displays in the console- helpful for debugging */
}
If you take the drawImg stuff and shove it in its own function you can clean this up a bit :) Now we've yanked the async stuff out of the loop, so the image variable doesn't get over-written each time you loop. You're also using a for loop now, which to me is clearer to understand.
function drawLevelOneElements() {
// your stuff
for (var i = 0; i > 5; i++) {
drawImg(ctx, i, x, y);
// update x or y and whatever else
}
}
// put all your image drawing stuff here
function drawImg(ctx, i, x, y) {
var img = new Image();
img.src = i + ".png";
img.onload = function(){
ctx.drawImage(img, x, y, 50, 50);
}
}

Categories