Can't load images on canvas for my js game - javascript

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>

Related

How to copy canvas image data to some other variable?

I am working on a small app, that loads user image onto a server, lets him choose one of the filters and gives image back.
I need to somehow save the initial image data with no filters applied.
But as i found out, in JS there is no natural way to copy vars.
I tried using LoDash _.clone() and one of the jQuery functions to do this, but they didn't work.
When I applied a cloned data to image, function putImageData couldn't get the cloned data because of the wrong type.
It seems, that clone functions somehow ignore object types.
Code:
var img = document.getElementById("image");
var canvas = document.getElementById("imageCanvas");
var downloadLink = document.getElementById("download");
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, img.width, img.height);
document.getElementById("image").remove();
initialImageData = context.getImageData(0, 0, canvas.width, canvas.height); //initialImageData stores a reference to data, but I need a copy
///////////////////////
normalBtn.onclick = function(){
if(!(currentState == converterStates.normal)){
currentState = converterStates.normal;
//here I need to apply cloned normal data
}
};
So, what can I do here???
Thanks!!!
The correct way to copy a typed array is via the static function from
eg
var imageData = ctx.getImageData(0,0,100,100);
var copyOfData = Uint8ClampedArray.from(imageData.data); // create a Uint8ClampedArray copy of imageData.data
It will also allow you to convert the type
var copyAs16Bit = Uint16Array.from(imageData.data); // Adds high byte. 0xff becomes 0x00ff
Note that when converting to a smaller type the extra bits are truncated for integers. When converting from floats the value not the bits are copied. When copying between signed and unsigned ints the bits are copied eg Uint8Array to Int8Array will convert 255 to -1. When converting from small int to larger uint eg Int8Array to Uint32Array will add on bits -1 becomes 0xffff
You can also add optional map function
// make a copy with aplha set to half.
var copyTrans = Uint8ClampedArray.from(imageData.data, (d, i) => i % 4 === 3 ? d >> 1 : d);
typedArray.from will create a copy of any array like or iterable objects.
Use :
var image = …;
var data = JSON.parse(JSON.stringify(image).data);
var arr = new Uint8ClampedArray(data);
var copy = new ImageData(arr, image.width, image.height);
An ImageData object holds an Uint8ClampedArray which itself holds an ArrayBuffer.
To clone this ArrayBuffer, you can use its slice method, or the one from the TypedArray View you get :
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'orange';
ctx.fillRect(0,0,300,150);
var original = ctx.getImageData(0,0,300,150);
var copiedData = original.data.slice();
var copied = new ImageData(copiedData, original.width, original.height);
// now both hold the same values
console.log(original.data[25], copied.data[25]);
// but can be modified independently
copied.data[25] = 0;
console.log(original.data[25], copied.data[25]);
<canvas id="canvas"></canvas>
But in your case, an easier solution, is to call twice ctx.getImageData.
var ctx = canvas.getContext('2d');
ctx.fillStyle = 'orange';
ctx.fillRect(0,0,300,150);
var original = ctx.getImageData(0,0,300,150);
var copied = ctx.getImageData(0,0,300,150);
// both hold the same values
console.log(original.data[25], copied.data[25]);
// and can be modified independently
copied.data[25] = 0;
console.log(original.data[25], copied.data[25]);
<canvas id="canvas"></canvas>
And an complete example :
var ctx = canvas.getContext('2d');
var img = new Image();
// keep these variables globally accessible to our script
var initialImageData, filterImageData;
var current = 0; // just to be able to switch easily
img.onload = function(){
// prepare our initial state
canvas.width = img.width/2;
canvas.height = img.height/2;
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
// this is the state we want to save
initialImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
// get an other, independent, copy of the current state
filterImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
// now we can modify one of these copies
applyFilter(filterImageData);
button.onclick = switchImageData;
switchImageData();
}
// remove red channel
function applyFilter(image){
var d = image.data;
for(var i = 0; i < d.byteLength; i+=4){
d[i] = 0;
}
}
function switchImageData(){
// use either the original one or the filtered one
var currentImageData = (current = +!current) ?
filterImageData : initialImageData;
ctx.putImageData(currentImageData, 0, 0);
log.textContent = current ? 'filtered' : 'original';
}
img.crossOrigin = 'anonymous';
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
<button id="button">switch imageData</button>
<code id="log"></code><br>
<canvas id="canvas"></canvas>
The same with slice:
var ctx = canvas.getContext('2d');
var img = new Image();
// keep these variables globally accessible to our script
var initialImageData, filterImageData;
var current = 0; // just to be able to switch easily
img.onload = function(){
// prepare our initial state
canvas.width = img.width/2;
canvas.height = img.height/2;
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
// this is the state we want to save
initialImageData = ctx.getImageData(0,0,canvas.width,canvas.height);
// get an other, independent, copy of the current state
filterImageData = new ImageData(initialImageData.data.slice(), initialImageData.width, initialImageData.height);
// now we can modify one of these copies
applyFilter(filterImageData);
button.onclick = switchImageData;
switchImageData();
}
// remove red channel
function applyFilter(image){
var d = image.data;
for(var i = 0; i < d.byteLength; i+=4){
d[i] = 0;
}
}
function switchImageData(){
// use either the original one or the filtered one
var currentImageData = (current = +!current) ?
filterImageData : initialImageData;
ctx.putImageData(currentImageData, 0, 0);
log.textContent = current ? 'filtered' : 'original';
}
img.crossOrigin = 'anonymous';
img.src = 'https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg';
<button id="button">switch imageData</button>
<code id="log"></code><br>
<canvas id="canvas"></canvas>

Moving image on canvas on the X axis only

The Problem
I am finding it rather difficult to get my head around this, I am attempting to move an image using the mouse along the X axis only. I am finding it hard to even move the image at all and the many tutorials I have looked at arnt really helping me. Here is what I am trying to say:
As you can see by my beautiful image above I only want to image to move left and right at the bottom of the page.
The Code and the Question
Here is my first attempt, when I try this all the images loaded on the canvas no longer appear making it very hard for me to understand why it isnt working.
<script type="text/javascript">
//Referencing the canvas
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var width = canvas.getAttribute('width');
var height = canvas.getAttribute('height');
//Images
var bggameImage = new Image();
var playerImage = new Image();
var enemyImage = new Image();
var projectileImage = new Image();
var livesImage = new Image();
//Canvas dimensions
var width = 480;
var height = 320;
//Loading in the backgroundImage
bggameImage.src = "Images/bggameImage.png";
bggameImage.onload = function(){
context.drawImage(bggameImage, 0, 0);
}
//Loading in the playerImage
playerImage.src = "Images/playerImage.png";
playerImage.onload = function(){
context.drawImage(playerImage, 165, 240);
}
//Loading in the projectileImage
projectileImage.src = "Images/projectileImage.png";
projectileImage.onload = function(){
context.drawImage(projectileImage, 65, 240);
}
var playerImage = {
x:176,
y:74,
}
function init() {
playerImage.src = "Images/playerImage.png";
//Moving player
myCanvas.addEventListener("mousemove", function (e) {
var bounding_box = myCanvas.getBoundingClientRect();
playerImage = (e.clientX - bounding_box.left) * (myCanvas.width / bounding_box.width) - playerImage.width / 2;
playerImage = (e.clientY - bounding_box.top) * (myCanvas.height / bounding_box.height) - playerImage.height / 2;
}
)
</script>
The whole "function init()" part is what I have just tried but I thought I would include this anyway, I understand that I am loading in the playerImage twice.
You're using the same variable name twice (playerImage), so your image is being overwritten. You're using it for the image and also to store the position. Change the playerImage that's storing x and y to be playerPosition or something like that. Update that variable on your mouse event and then render the image according to that variable's values.
Ultimately, you're going to have to look at a game loop using setTimeout or requestAnimationFrame. So, this will become crucial at that stage. And yes, you shouldn't be loading the player image twice either. Do all of that at the start and only start your game when all your assets have successfully loaded.
For instance...
var playerImage;
var alienImage;
var bulletImage;
var assetCount = 0;
function loadAssets() {
playerImage = new Image();
playerImage.onload = checkAssetsLoaded;
playerImage.src = "assets/images/Brush01.png";
alienImage = new Image();
alienImage.onload = checkAssetsLoaded;
alienImage.src = "assets/images/Brush02.png";
bulletImage = new Image();
bulletImage.onload = checkAssetsLoaded;
bulletImage.src = "assets/images/Brush03.png";
}
function checkAssetsLoaded(event) {
assetCount++;
console.log("An asset has loaded!: " + assetCount);
if (assetCount == 3) {
startGame();
}
}
function startGame() {
// Start your game initialization logic here.
console.log("Game starting!");
}

How to animate sprites on keypress on canvas?

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.

Multiple Canvas images appearing randomly

I am trying to apply different images to multiple canvas.
The images are passed into a json array that is generated in the code behind and then looped through drawing to there specific canvas.
Now this appears to work on occasion sometimes rendering sometimes not.
So here is the code.
function renderPlayer() {
var imagesList = <%=imageList %>;
var size = imagesList.length;
for(var key in imagesList)
{
var image = imagesList[key][1];
var gridPosition = imagesList[key][0];
var playerCanvas = document.getElementById(gridPosition);
window.context2 = playerCanvas.getContext("2d");
//context2.save();
playerCanvas.setAttribute("width", 97);
var imageObj = new Image();
imageObj.src = image;
context2.drawImage(imageObj, 0, 10, 97, 97);
}
}
The above code places them where I expect them to but no every time the page is rendered.
However the bellow code appears to only render the last image.
function renderPlayer() {
var imagesList = <%=imageList %>;
var size = imagesList.length;
for(var key in imagesList)
{
var image = imagesList[key][1];
var gridPosition = imagesList[key][0];
var playerCanvas = document.getElementById(gridPosition);
window.context2 = playerCanvas.getContext("2d");
//context2.save();
playerCanvas.setAttribute("width", 97);
var imageObj = new Image();
imageObj.onload = function(){
context2.drawImage(imageObj, 0, 10, 97, 97);
};
imageObj.src = image;
}
}
Does anyone have any suggestions why this may not be working?
Same old problem... all of your image onload methods refer to one and only imageObj that gets overwritten every time through the loop.
for(var key in imagesList)
{
var image = imagesList[key][1];
var gridPosition = imagesList[key][0];
var playerCanvas = document.getElementById(gridPosition);
window.context2 = playerCanvas.getContext("2d");
//context2.save();
playerCanvas.setAttribute("width", 97);
(function(){
var imageObj = new Image();
imageObj.onload = function(){
context2.drawImage(imageObj, 0, 10, 97, 97);
};
imageObj.src = image;
})();
}
by wrapping in a immediately executing function block, each imageObj becomes it's own variable, which each onload function captures.
The first example is failing because you need to wait for the image to be loaded before you can draw it in the canvas. The randomness is caused by the fact that sometimes the image will have loaded by the time you run the context2.drawImage(..) command and sometimes it won't, by placing this code inside the onload handler of the image you should guarantee that the image data is present.
The second example looks better, but you should change onLoad to onload and move the lines
var playerCanvas = document.getElementById(gridPosition);
var context2 = playerCanvas.getContext("2d");
outside the for loop, also note the change to the second line.

Javascript Canvas Element - Array Of Images

I'm just learning JS, trying to do things without jQuery, and I want to make something similar to this however I want to use an array of images instead of just the one.
My image array is formed like this
var image_array = new Array()
image_array[0] = "image1.jpg"
image_array[1] = "image2.jpg"
And the canvas element is written like this. (Pretty much entirely taken from the Mozilla site)
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.src = 'sample.png';
img.onload = function(){
for (i=0;i<5;i++){
for (j=0;j<9;j++){
ctx.drawImage(img,j*126,i*126,126,126);
}
}
}
}
It uses the image "sample.png" in that code but I want to change it to display an image from the array. Displaying a different one each time it loops.
Apoligies if I've not explained this well.
Just iterate over the array, and position the images by using its width and height properties:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d'),
img, i, image_array = [];
image_array.push("http://sstatic.net/so/img/logo.png");
image_array.push("http://www.google.com/intl/en_ALL/images/logo.gif");
// ...
for (i = 0; i < image_array.length; i++) {
img = new Image();
img.src = image_array[i];
img.onload = (function(img, i){ // temporary closure to store loop
return function () { // variables reference
ctx.drawImage(img,i*img.width,i*img.height);
}
})(img, i);
}
}
Check this example.

Categories