draw image on canvas after load into array - javascript

I tried to create an array of Image to be displayed on a canvas, after each image is loaded. No errors, no draw...
var x=...
var y=...
var canvas = document.getElementById(sCanvasName);
var context = canvas.getContext('2d');
var imageCardObj = [];
//vCards contains the images file names
for (var k=0;k<vCards.length;k++){
imageCardObj[k] = new Image();
var func = function(){
var c = arguments[3];
try{
c.drawImage(arguments[0], arguments[1], arguments[2]);
}catch(e){
alert(e.message)
}
}
imageCardObj[k].onload = func(imageCardObj[k], x, y, context);
imageCardObj[k].src = "res/img/"+vCards[k].trim()+".png";
x+=40;
}

You are calling the func() handler and gives the result to it to the image's onload handler. That won't work so well.. and you cannot pass arguments that way to a handler function.
Try this:
var func = function(){
// "this" will be the current image in here
var c = arguments[3];
try{
c.drawImage(this, x, y); // you need to reference x and y
}catch(e){
alert(e.message)
}
}
imageCardObj[k].onload = func; // only a reference here
If you need different x and y's then you need to maintain those on the side, either in an additional array or use objects to embed the image, its intended x and y and use the url to identify the image in question inside the func() callback.
Also note that load order may vary as the last image loaded could finish before the first one so when you draw the image they may not appear in the same order.
You may want to do this instead:
var files = [url1, url2, url, ...],
images = [],
numOfFiles = files.length,
count = numOfFiles;
// function to load all images in one go
function loadImages() {
// go through array of file names
for(var i = 0; i < numOfFiles; i++) {
// create an image element
var img = document.createElement('img');
// use common loader as we need to count files
img.onload = imageLoaded;
//img.onerror = ... handle errors too ...
//img.onabort = ... handle errors too ...
img.src = files[i];
// push image onto array in the same order as file names
images.push(img);
}
}
function imageLoaded(e) {
// for each successful load we count down
count--;
if (count === 0) draw(); //start when all images are loaded
}
Then you can start the drawing after the images has loaded - the images are now in the same order as the original array:
function draw() {
for(var i = 0, img; img = images[i++];)
ctx.drawImage(img, x, y); // or get x and y from an array
}
Hope this helps!

This is the final (working) version
var x=...
var y=...
var canvas = document.getElementById(sCanvasName);
var context = canvas.getContext('2d');
var imageCardObj = [];
for (var k=0;k<vCards.length;k++){
imageCardObj[k] = new Image();
imageCardObj[k].xxx=x;
var func = function(){
try{
context.drawImage(this, this.xxx, yStreet);
}catch(e){
alert(e.message)
}
}
imageCardObj[k].onload = func;
imageCardObj[k].src = 'res/img/'+vCards[k].trim()+".png";
x +=40;

Related

How to add a 16x16 grid of pseudo-random tiles as a background in canvas (or other web language)

I have recently been working on a web based project using canvas on HTML5. The program consists of a 16x16 grid of tiles that have been pseudo-randomly generated. I am relatively new to canvas, but have built this program in several other environments, none of which however compile successfully to a web based language. this is the main code section that is giving me bother:
var A = 8765432352450986;
var B = 8765432352450986;
var M = 2576436549074795;
var X = 1;
var rx = 0;
var ry = 0;
this.image = new Image();
var i = 0;
var ii = 0;
while(i < 16)
{
while(ii < 16)
{
this.image = new Image();
this.image.src = "textures/grass.png";
x = (((A*X)+B)%M)%M;
if((x/2)%1 == 0)
{
this.image.src = "textures/grass.png";
}
if((x/8)%1 == 0)
{
this.image.src = "textures/hill.png";
}
if((x/21)%1 == 0)
{
this.image.src = "textures/trees.png";
}
if((x/24)%1 == 0)
{
this.image.src = "textures/sea.png";
}
if((x/55)%1 == 0)
{
this.image.src = "textures/mountain.png";
}
if((x/78)%1 == 0)
{
this.image.src = "textures/lake.png";
}
if((x/521)%1 == 0)
{
this.image.src = "textures/volcano.png";
}
if((x/1700)%1 == 0)
{
this.image.src = "textures/shrine.png";
}
if((x/1890)%1 == 0)
{
this.image.src = "textures/outpost.png";
}
if((x/1999)%1 == 0)
{
this.image.src = "textures/civ.png";
}
ctx = myGameArea.context;
ctx.drawImage(this.image,rx, ry, 20, 20);
ii ++;
rx += 20;
}
i ++;
rx = 0;
ry += 16;
}
I would like canvas to draw along the lines of this code above, effectively generating a grid like this
pre generated grid image
(please try and ignore the obvious bad tile drawings, I planned on either finding an artist or trying slightly harder on them when I get the game fully working.)
The black square is a separate movable object. I haven't got as far as implementing it in this version, but if you have any suggestions for it please tell me
in the full html file I have now, the canvas renders but none of the background (using the w3schools tutorials, I can make objects render however)
In short: how do I render a background consisting of a 16x16 grid of pseudo-random tiles on an event triggered or on page loaded, using canvas or if that does not work another web based technology
Thank you for your time.
A few problems but the main one is that you need to give an image some time to load before you can draw it to the canvas.
var image = new Image();
image.src = "image.png";
// at this line the image may or may not have loaded.
// If not loaded you can not draw it
To ensure an image has loaded you can add a onload event handler to the image
var image = new Image();
image.src = "image.png";
image.onload = function(){ ctx.drawImage(image,0,0); }
The onload function will be called after all the current code has run.
To load many images you want to know when all have loaded. One way to do this is to count the number of images you are loading, and then use the onload to count the number of images that have loaded. When the loaded count is the same as the loading count you know all have loaded and can then call a function to draw what you want with the images.
// Array of image names
const imageNames = "grass,hill,trees,sea,mountain,lake,volcano,shrine,outpost,civ".split(",");
const images = []; // array of images
const namedImages = {}; // object with named images
// counts of loaded and waiting toload images
var loadedCount = 0;
var imageCount = 0;
// tile sizes
const tileWidth = 20;
const tileHeight = 20;
// NOT SURE WHERE YOU GOT THIS FROM so have left it as you had in your code
// Would normally be from a canvas element via canvasElement.getContext("2d")
var ctx = myGameArea.context;
// seeded random function encapsulated in a singleton
// You can set the seed by passing it as an argument rand(seed) or
// just get the next random by not passing the argument. rand()
const rand = (function(){
const A = 8765432352450986;
const B = 8765432352450986; // This value should not be the same as A?? left as is so you get the same values
const M = 2576436549074795;
var seed = 1;
return (x = seed) => seed = ((A * x) + B) % M;
}());
// function loads an image with name
function addImage(name){
const image = new Image;
image.src = "textures/" + name + ".png";
image.onload = () => {
loadedCount += 1;
if(loadedCount === imageCount){
if(typeof allImagesLoaded === "function"){
allImagesLoaded();
}
}
}
imageCount += 1;
images.push(image);
namedImages[name] = image;
}
imageNames.forEach(addImage); // start loading all the images
// This function draws the tiles
function allImagesLoaded(){ /// function that is called when all the images have been loaded
var i, x, y, image;
for(i = 0; i < 256; i += 1){ // loop 16 by 16 times
ctx.drawImage(
images[Math.floor(rand()) % images.length]; //random function does not guarantee an integer so must floor
(i % 16) * tileWidth, // x position
Math.floor(i / 16) * tileHeight, // y position
tileWidth, tileHeight // width and height
);
}
}

dynamic image variable with onclick event

I have several canvases. I also have several picture URLs. I want to draw all pictures on the canvas. There is a problem in the drawing function. Drawing the image only works when the image loads completely, but I have to draw the image as it loads. I wrote following code:
for (var i = 2; i < length; i++) {
canvid[i] = "canv" + i;
img[i] = new Image();
img[i].src = "..\\images\\UploadImage\\"+ name + i + ".jpg";
img[i].onload = function () {
var c = document.getElementById(canvId[i]);
var cDraw = c.getContext("2d");
cDraw.drawImage(img[i], 0, 0);
};
I know this code has error, it's kind of pseudo code to show what I want.
Put your logic in
$(documet).ready(function(){
//logic
});
the answer is in following link
stack overflow link
when you want to call on click event on image variable you have to wait for it
so you couldn't use loop you have to put next call on previous image on load event .
var loadImages = function (imageURLarray) {
if (!(startPage < pages))
return;
canvid = "canv" + i;
img.src = imageURLarray[startPage];
// your top code
img.onload = function (e) {
// code, run after image load
var c = document.getElementById(canvid);
var cDraw = c.getContext("2d");
cDraw.drawImage(img, 0, 0);
startPage++;
loadImages(imageURLarray);
}
}
loadImages(imageURLarray);

drawImage in JavaScript (Canvas)

I have an array of image objects which hold all the necessary info like path,x, y, w, h. Now i want to draw all those images on canvas in a loop.. but when i do so, it only draws the first image. Here is the code:
for(var i=0; i<shapes.length; i++)
{
var A = shapes.pop();
if(A.name == 'image' && A.pageNum == pNum)
{
var img = new Image();
img.src = A.path;
img.onload = function() {
context.drawImage(img, A.x, A.y, A.w, A.h);
}
}
}
i checked all the info in the shapes array... inside the if condition, before calling drawImage function, all the info of each image is correct but for some strange reason it doesn't display images except the 'last' in the array (the one which pops out last)
Your image loading code is faulty.
Each image will take time to load and by then you have overwritten var img with another new Image();
Here's an example of an image loader that executes only after all the images have been loaded and are ready to be drawn:
[ Warning: untested code -- some adjustments may be required! ]
// image loader
var imageURLs=[]; // put the paths to your images in this array
var imagesOK=0;
var imgs=[];
imageURLs.push("");
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
// note: instead of this last line, you can probably use img.src=shapes[i].path;
}
}
function start(){
// the imgs[] array holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
for(var i=0;i<shapes.length;i++){
var shape=shapes[i];
context.drawImage(imgs[i],shape.x,shape.y,shape.w,shape.h);
}
}
It was the problem of some sort of closure... like, the loop finishing before images getting loaded or something. The solution that worked for me was putting all the image loading code in a separate function and calling that function from the loop:
if(A.name == 'image' && A.pageNum == pNum)
{
displayImage(A.path, A.x, A.y, A.w, A.h);
}
function displayImage(path, x, y, w, h)
{
var img = new Image();
img.src = path;
img.onload = function() {
context.drawImage(img, x, y, w, h);
}
}
I don't understand why you're using pop() to get your object data. You could instead access each object using shapes[i] notation and, as a bonus, store image handles in each object:
for(var i=0; i<shapes.length; i++)
{
if(shapes[i].name == 'image' && shapes[i].pageNum == pNum)
{
shapes[i].img = new Image();
shapes[i].img.src = shapes[i].path;
shapes[i].img.onload = function() {
context.drawImage(shapes[i].img, shapes[i].x, shapes[i].y, shapes[i].w, shapes[i].h);
}
}
}

Canvas stroke text for every object in array but draw just one image

I am trying to draw small image on canvas and some text for every person which I have in array.
I am doing like this
function drawPersonsOnCanvas(canvas, persons) {
var workIcon = '../images/work.png';
var partyIcon = '../images/party.png';
var context = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
// randomizePositions just put every object of array on random position inside canvas
persons = randomizePositions(persons, width, height);
for (var i in persons) {
var person= persons[i];
person['image'] = new Image();
person['image'].src = (person['type'] === 'work') ? workIcon: partyIcon;
person['image'].onload = function() {
context.drawImage(person['image'], person['x'], person['y']);
};
context.strokeText('person ' + person['status'], person['x'], person['y']);
}
}
and I always get all texts on map on good positions and just one image and x and y are different for all ( I have 5 persons in array, I tried and without onload but it doesn't work). Does anyone know what to change so I get one picture for every person?
By the time each onload is executed, your person variable has already been reassigned to the next element in persons.
So you need to create a new person variable inside onload.
To do this, add an index to your person[‘image’] that points to the correct person.
…
person['image'].index=i;
person['image'].src = (person['type'] === 'work') ? workIcon: partyIcon;
…
Then inside onload you can recreate the correct person:
var person=persons[this.index];
So your reworked code looks like this...and a Fiddle: http://jsfiddle.net/m1erickson/xms9B/
function drawPersonsOnCanvas(canvas, persons) {
var workIcon = '../images/work.png';
var partyIcon = //'../images/party.png';
var context = canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
// randomizePositions just put every object of array on random position inside canvas
persons = randomizePositions(persons, width, height);
for (var i in persons) {
var person= persons[i];
person['image'] = new Image();
person['image'].onload = function() {
var person=persons[this.index];
context.drawImage(person['image'], person['x'], person['y']);
context.strokeText('person ' + person['status'], person['x'], person['y']);
};
person['image'].index=i;
person['image'].src = (person['type'] === 'work') ? workIcon: partyIcon;
}
}

create an asynchronous each loop in jquery

This is my each loop:-
var image_obj = {};
$(".wrapper").each(function (index, data) {
var dfile = this.getElementsByClassName('image')[0];
file = dfile.files[0];
if(file != null) {
var fr = new FileReader();
fr.onload = function (e) {
img = new Image();
img.onload = function (k) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
objindex = "obj_" + index;
image_obj[objindex] = canvas.toDataURL("image/jpeg");
};
img.src = fr.result;
};
fr.readAsDataURL(file);
}
});
I need the index of my each loop to save base_64 encoded image to an object.
But the index is not showing up in order as each loop execution finishes before reaching canvas.getContext("2d");.
One big problem is that you need to declare img inside your outer function:
$(".wrapper").each(function (index, data) {
var img;
The reason is that otherwise, img is a global. The img variable captured in your onload function just contains the current value of that global, which is just whatever the most recent each call assigned it to (likely the last wrapper in the jquery object). Then when onload is called, it writes the wrong image into the canvas. By declaring the variable, you ensure that each outer function scope has its very own img variable for your onload functions to capture, which they'll then use when they're actually applied.
Edit If you want to ensure that the outputed order is right, you should just sort it out at the end, since you don't control when onload runs; that's actually the beauty of it. I'd do something like this:
ctx.drawImage(img, 0, 0);
if (typeof(image_obj.images) == "undefined")
image_obj.images = [];
image_obj.images[index] = canvas.toDataURL("image/jpeg");
Or just make image_obj itself an array and just do:
ctx.drawImage(img, 0, 0);
image_arr[index] = canvas.toDataURL("image/jpeg");
Depending on whether you need the object as a container for other stuff.
Since that's an array, not an object, the images will be in order.
Edit 2
So the problem now is that you get holes in your array if some of the files aren't there. Let's make that not happen:
var index = -1;
$(".wrapper").each(function (_, data) {
...
if(file != null) {
var fr = new FileReader();
index++;
var localIndex = index; //to capture locally
fr.onload = function (e) {
...
ctx.drawImage(img, 0, 0);
image_arr[localIndex] = canvas.toDataURL("image/jpeg");
..

Categories

Resources