Make chain img.onload with img.src to Image() object - javascript

I have a function able to read array of "links of images" with foreach(item) iteration. In each iteration I am trying to: load image in an Image() object, and in the onload event to get properties (naturalWidth & naturalHeight) and paste it in the DOM as background of <DIV>.
However img.onload is asynchronous and before onload event pastes ones all iterations will pass.
How can I make continuous chain of img.onload and changing of img.src in each iteration?
function singleGrid() {
galdiv1.className = galClassList;
function imgFill() {
arr.forEach(function(item) {
img = new Image();
img.onload = function() {
var nHeight = this.naturalHeight;
var nWidth = this.naturalWidth;
var res = nWidth / nHeight;
console.log("image source: " + arr[i] + ", naturalWidth: " + nWidth + "px, naturalHeight: " + nHeight + "px");
blockres = width * res;
var div = divCreate(blockres);
galdiv1.append(div);
};
img.src = item;##
Heading##
}, this);
}
imgFill();
}

var imageURLS = [];
var imageTags = [];
// Runs once when all images are loaded
function onAllImagesLoaded() {
}
// Individual image loaded Successfully
function onImageLoad() {
this.hasLoaded = true;
for (var i = 0; i < imageTags.length; ++i) {
if (!imageTags[i].hasLoaded) {
return; // Exit the function if any image hasn't loaded yet
}
}
onAllImagesLoaded();
}
// Individual image failed to load
function onImageError() {
console.warn("Error: image " + this.id + " could not be retrieved");
this.hasLoaded = true;
for (var i = 0; i < imageTags.length; ++i) {
if (!imageTags[i].hasLoaded) {
return;
}
}
onAllImagesLoaded();
}
function loadImages() {
var newImageTag = null;
for (var i = 0; i < imageURLS.length; ++i) {
newImageTag = new Image();
newImageTag.id = i; // Give each Image a unique identifier (Helps to find ones that don't load properly if you have any problems)
newImageTag.hasLoaded = false; // Add new 'hasLoaded' flag
newImageTag.onload = onImageLoad; // Set event handlers, It's better to use an existing function instead of an inlined one
newImageTag.onerror = onImageError; // This is because an inlined function will create a closure when called
newImageTag.src = imageURLS[i]; // This starts the async loading
imageTags.push(newImageTag);
}
}

Related

how to use drawImage after load all images in array

I want to draw image array with drawImage after all the images are loaded.There is a render problem with drawImage(), tried to solve with setTimeout() but its not working all the time.
Here is my code;
while(FocusItem.length>0)
{
FocusItem.pop();
}
ftx=canvas.getContext('2d');
focusImageBackground = new Image();
focusImageBackground.src = "./images/odaklanma/odaklanmaBackground.jpg";
if(RandomSoru==15)
finishSoru=true;
if(finishSoru)
{
RandomSoru = Math.floor((Math.random() * 15)+1);
tempRandomSoru=RandomSoru;
}
if(RandomSoru==tempRandomSoru)
{
RandomSoru = Math.floor((Math.random() * 15)+1);
}
var soru = new Object();
soru["image"] = new Image();
soru.image.src = './images/odaklanma/level/'+RandomSoru+'/soru.png';
soru["x"] = 341;
soru["y"] = 140;
FocusItem.push(soru);
var dogru = new Object();
dogru["image"] = new Image();
dogru.image.src = './images/odaklanma/level/'+RandomSoru+'/dogru.png';
dogru["x"] = xDogru;
dogru["y"] = 280;
FocusItem.push(dogru);
var yanlis = new Object();
yanlis["image"] = new Image();
yanlis.image.src = './images/odaklanma/level/'+RandomSoru+'/yanlis.png';
yanlis["x"] = xYanlis1;
yanlis["y"] = 280;
FocusItem.push(yanlis);
var yanlis2 = new Object();
yanlis2["image"] = new Image();
yanlis2.image.src = './images/odaklanma/level/'+RandomSoru+'/yanlis1.png';
yanlis2["x"] = xYanlis2;
yanlis2["y"] = 280;
FocusItem.push(yanlis2);
}
if(focusImageBackground.complete){
if(FocusItem[0].image.complete && FocusItem[1].image.complete && FocusItem[2].image.complete && FocusItem[3].image.complete)
drawFocus();
else
setTimeout(drawFocus,600);
}
else
focusImageBackground.onload=function(){
if(FocusItem[0].image.complete && FocusItem[1].image.complete && FocusItem[2].image.complete && FocusItem[3].image.complete)
drawFocus();
else
setTimeout(drawFocus,600);
}
function drawFocus(){
ftx.drawImage(focusImageBackground,0,0);
for (var i=0; i<FocusItem.length; i++){
FocusItem[i].image.onload=function(){
ftx.drawImage (FocusItem[i].image, FocusItem[i].x, FocusItem[i].y);
}
}
}
I'd suggest loading all your images, then when they are all done, you can call the rest of your code. I don't quite follow what you're trying to do with all the rest of your code, but here's a simple way to load an array of image URLs and know when they are done.
This is the general idea (I've left out lots of your code that has nothing to do with the central issue of knowing when all the images are loaded) and I've also tried to DRY up your code:
function createImagesNotify(srcs, fn) {
var imgs = [], img;
var remaining = srcs.length;
for (var i = 0; i < srcs.length; i++) {
img = new Image();
imgs.push(img);
img.onload = function() {
--remaining;
if (remaining == 0) {
fn(srcs);
}
};
// must set .src after setting onload handler
img.src = srcs[i];
}
return(imgs);
}
// here's your starting array of filenames
var fnames = ["soru.png", "dogru.png", "yanlis.png", "yanlis1.png"];
// insert your process to create RandomSoru here
var randomSoru = ....;
// build full urls array
var urls = [];
for (var i = 0; i < fnames; i++) {
urls.push('./images/odaklanma/level/' + RandomSoru + '/' + fnames[i]);
}
// load all images and call callback function when they are all done loading
var imgs = createImagesNotify(urls, function() {
// all images have been loaded here
// do whatever you want with all the loaded images now (like draw them)
});
This code is based on an earlier answer of mine here: Cross-browser solution for a callback when loading multiple images?

loading an unknown number of images

I'm trying to create a lightbox for my site, and I want it to load all the images from a given directory with a filename like image#.jpg.
This is the code I have:
for(var i=0; i<1000; i++)
{
var filename = "images/image"+i+".jpg";
$.get(filename)
.done(function() {
$('#lightbox').append('<img src="placeholder.gif">');
})
.fail(function() {
i=1000; //ugh
});
}
It kind of works, but only tries to load image1000.jpg.
Also, is there a better way to do something like this? I'm sure saying 'do this a ton of times and stop when I manually change the for loop counter' is frowned on.
If your image names are sequential like your said, you can create a loop for the names, checking at every iteration if image exists - and if it doesn't - break the loop:
var bCheckEnabled = true;
var bFinishCheck = false;
var img;
var imgArray = new Array();
var i = 0;
var myInterval = setInterval(loadImage, 1);
function loadImage() {
if (bFinishCheck) {
clearInterval(myInterval);
alert('Loaded ' + i + ' image(s)!)');
return;
}
if (bCheckEnabled) {
bCheckEnabled = false;
img = new Image();
img.onload = fExists;
img.onerror = fDoesntExist;
img.src = 'images/myFolder/' + i + '.png';
}
}
function fExists() {
imgArray.push(img);
i++;
bCheckEnabled = true;
}

how to assign images using javascript to div

function ft1(){
var imgSrcs = ['1.gif','2.gif','3.gif','4.gif','5.gif','6.gif','7.gif','8.gif'];
var myImages = [], img;
for (var i = 1; i <=8; i++) {
img = new Image();
img.onload = function() {
var div0 = document.getElementById(i);
div0.style.backgroundImage = "url(" + this.src + ")";
};
img.src = imgSrcs[i];
myImages[i] = img;
}
}
<input name="Button1" type="button" value="button" onclick="ft1();" />
my div ids are 1 to 8. i want to add images for that dives using javascript.but this code didn't work properly. if u know where is the error plz tel me.
I made some changes in your code:
function ft1() {
var imgSrcs = ['1.gif', '2.gif', '3.gif', '4.gif', '5.gif', '6.gif', '7.gif', '8.gif'];
var myImages = [];
for (var i = 1; i <= imgSrcs.length; i++) {
var img = new Image();
img.src = imgSrcs[i];
var div0 = document.getElementById(i);
//I am not sure what you are trying to do in the below line.
//div0.style.backgroundImage = "url(" + this.src + ")";
div0.appendChild(img);
myImages[i] = img;
}
}
AFAIK, img.onload will not work because img variable is not part of DOM. First you need to make it part of DOM using appendChild method as shown above.
I have one question, what does this.src referring to?
try using this code in the loop
var elem = document.createElement("img");
elem.src = this.src;
elem.setAttribute("height", "250");
elem.setAttribute("width", "1024");
elem.setAttribute("alt", "alt text");
document.getElementById("placehere").appendChild(elem);
good luck!
I think You should use imgSrcs[i] instead of this.src
<script type="text/javascript">
var myImages = new Array("usa.gif","canada.gif","jamaica.gif","mexico.gif");// list of images
function changeImg(that) // function call on_click of image below
{
var newImgNumber = Math.round(Math.random()*3); // create math to random the images
while (that.src.indexOf(myImages[newImgNumber]) != -1) // make sure that the randomization corresponds to the list of images
{
newImgNumber = Math.round(Math.random()*3) // sets the math to the new image
}
that.src = myImages[newImgNumber]; // change the image
return false; // makes it able to be done again
}
</script>
If you are trying to add images to all the divs, I would try simplifying it to:
function ft1(){
var imgSrcs = ['1.gif','2.gif','3.gif','4.gif','5.gif','6.gif','7.gif','8.gif'];
for (i = 1; i <=8; i++) {
var div = document.getElementById(i);
div.style.backgroundImage = "url(" + imgSrcs[i] + ")";//make sure images are in the right folder
}
}
<input name="Button1" type="button" value="button" onclick="ft1()" /> //remove semicolon
Here is a link to a jsfiddle with color instead of images http://jsfiddle.net/CKFrantz/gLfXA/
The problem with the loop is probably that the loop-counter will be the same in all of the callbacks. (Mind that the closures just share the same scope, so the value will be 8 for all of them after the loop is exited.)
This can be overcome by a factory function (passing a simple value as argument will result in it being copied):
function loadImageToElement(id, imgscr) {
var img = new Image();
img.onload = function() {
var el = document.getElementById(id);
el.style.backgroundImage = "url(" + this.src + ")";
// we could use just the same:
// el.style.backgroundImage = "url(" + imgscr + ")";
};
img.src = imgscr;
return img;
}
function ft1(){
var imgSrcs = ['1.gif','2.gif','3.gif','4.gif','5.gif','6.gif','7.gif','8.gif'];
var myImages = [];
for (var i = 0; i < 8; i++) {
myImages[i] = loadImageToElement('div' + (i+1), imgSrcs[i]);
}
}
<input name="Button1" type="button" value="button" onclick="ft1();" />
Edit: you may want to check for img.complete:
function loadImageToElement(id, imgscr) {
var img = new Image();
var f = function() {
var el = document.getElementById(id);
el.style.backgroundImage = "url(" + imgsrc + ")";
}
img.src = imgscr;
if (img.complete) {
f();
}
else {
img.onload = f;
}
return img;
}
(Note: Image.complete is true, if the image is already cached. In this case the onload-event wont fire in some browsers.)

how to check if all images loaded? (no jQuery please)

Due to the nature of async execution in .onload, Javscript would not run in the order as appeared in the code. Following snippet as I leared from how to alert after all images loaded? would not work always, pass here and fail there, all depending on the sequence of browser execution. Any suggestion how to solve it? (no jQuery please, nor asking me why)
var imgLoaded = 0;
var imgToLoad = 10;
var onImgLoad = function()
{
imgLoaded++;
if(imgLoaded == imgToLoad)
{
alert("done"); //Call to our draw function
}
}
for(var i = 0; i < 10; i++)
{
images[i] = new Image();
images[i].onload = onImgLoad;
images[i].src = 'images/'+i+'.png';
}
Specifically, here is where the code fails:
imgLoaded++;
if(imgLoaded == imgToLoad) { }
As a result, sometimes the code in the IF condition might not get executed even if all images were loaded corrected.
See this fiddle : http://jsfiddle.net/wdBbX/
var images = [];
function loadImages(callBack)
{
var imgLoaded = 0;
var imgToLoad = 10;
var onImgLoad = function()
{
imgLoaded++;
if(imgLoaded == imgToLoad)
{
callBack(imgLoaded); //Call to our draw function
}
}
for(var i = 0; i < 10; i++)
{
images[i] = new Image();
images[i].onload = onImgLoad;
images[i].src = 'images/'+i+'.png';
}
}
loadImages(function(i){
alert(i);
});
i prefer the sequentially way in this case.
so you can add cool animations with css...
var current=1,toload=10;
function next(){
var img=document.createElement('img');
img.onload=function(){
current++;
current==(toload+1)?(
console.log('ALL IMAGES ARE LOADED');
// callback
):(
next()
)
document.body.appendChild(this);
}
img.src='img/'+current+'.jpg';
}
window.onload=next;
i also added the way to addan image array
var current=0,images=['img1.jpg','img2.jpg','img3.jpg','img4.jpg'];
function next(){
var img=document.createElement('img');
img.onload=function(){
current++;
current==images.length?(
console.log('ALL IMAGES ARE LOADED');
// callback
):(
next()
)
document.body.appendChild(this);
}
img.src=images[current];
}
window.onload=next;
if your not shure about all images add also a img.onerror with the same function as onload esle the loop stops.
if you don't understand something or you need it as a callback function just ask.
Here is a proper up-to-date solution, including example usage:
const preload = src => new Promise(function(resolve, reject) {
const img = document.createElement('img');
img.onload = function() {
resolve(img);
}
img.onerror = reject;
img.src = src;
});
const preloadAll = sources =>
Promise.all(
sources.map(
preload));
const sources = [
'https://i.picsum.photos/id/1000/5626/3635.jpg',
'https://i.picsum.photos/id/10/2500/1667.jpg',
'https://homepages.cae.wisc.edu/~ece533/images/cat.png',
'https://homepages.cae.wisc.edu/~ece533/images/airplane.png'
];
preloadAll(sources)
.then(images => console.log('Preloaded all', images))
.catch(err => console.error('Failed', err));
From here.

Why doesn't this Javascript image object get added to the image array?

I've been trying to create a small HTML5-based example, but I've ran into some problems. I want to render a rock on a <canvas> but this Javascript object won't work properly.
function ImageData() {
this.image_names = ["rock1"];
this.images = new Array();
this.loadResources = function () {
for (var i = 0; i < this.image_names.length; i++) {
var img = new Image();
img.onload = (function (a) {
a.images[a.image_names[i]] = img;
console.log(a.images); //log is successful, image is in array
})(this);
img.src = "images/" + this.image_names[i] + ".png";
}
}
}
It's use is to be as follows:
var a = new ImageData();
a.loadResources();
var rock1_image = a.images["rock1"]; //the "rock1.png" image
If you try accessing the object via console, you'll see that there is no image in the image array, after being certain the image loaded. I can't figure out why it's not working. I've looked over it multiple times.
EDIT: Here is my final, working result:
function ImageData() {
this.image_names = ["rock1"];
this.images = new Array();
this.loadResources = function (callback) {
for (var i = 0; i < this.image_names.length; i++) {
var img = new Image();
img.onload = (function (a, i) {
return function() {
a.images[a.image_names[i]] = img;
if (i == (a.image_names.length - 1)) callback();
};
})(this, i);
img.src = "images/" + this.image_names[i] + ".png";
}
}
}
I would venture to say that you are attempting to access it before it is added to your array. I would suggest adding a callback or something to your loadResources method to make sure that it's there before you attempt to access it.
this.loadResources = function (callback) {
for (var i = 0; i < this.image_names.length; i++) {
var img = new Image();
img.onload = (function (a, i) {
return function() {
a.images[a.image_names[i]] = img;
console.log(a.images); //log is successful, image is in array
callback();
};
})(this, i);
img.src = "images/" + this.image_names[i] + ".png";
}
}
then
var a = new ImageData();
var rock1_image;
a.loadResources(function() {
rock1_image = a.images["rock1"];
// do whatever you need to do with the image in here.
});
Also, what #Igor mentions. I totally missed that. I adjusted the code above so you could keep your this scope without having to set it elsewhere.
Updated to take care of the i issue brought up by #RobD.
Remove (this) after onload handler definition. You are actually calling this function right away, assigning undefined (since it returns nothing) as img.onload value.
In the code:
> function ImageData() {
> this.image_names = ["rock1"];
> this.images = new Array();
> this.loadResources = function () {
> for (var i = 0; i < this.image_names.length; i++) {
> var img = new Image();
> img.onload = (function (a) {
This function will be run immediately and does not return a value, so undefined will be assigned. So no point in the assignment, just run the code.
> a.images[a.image_names[i]] = img;
Images is an array that is used as a plain object. Better to use a plain object then.
> console.log(a.images); //log is successful, image is in array
> })(this);
The outer this must be passed in because of the IIFE. Remove the IIFE and there is no requirement to pass this. And if a reference to the instance is stored in the function, it won't matter how the function is called (i.e. you won't have to set this in the call).
> img.src = "images/" + this.image_names[i] + ".png";
> }
> }
> }
You might want something like:
function ImageData() {
this.image_names = ['rock1'];
this.images = {};
imageData = this;
this.loadResources = function (callback) {
for (var i = 0; i < imageData.image_names.length; i++) {
var img = new Image();
imageData.images[a.image_names[i]] = img;
img.src = 'images/' + this.image_names[i] + '.png';
if (callback) {
img.onload = callback;
}
}
}
}
var a = new ImageData();
a.loadResources(function(){console.log('loaded: ' + this.src);}); // loaded: …
window.onload = function() {
document.body.appendChild(a.images.rock1); // image appears in document
}
I haven't tested your code yet, but I guess this is the problem:
You are trying to access your image before it was loaded. You can use callback event handler after it was load like:
var data = new ImageData();
data.loadResources(function(imgObj){
// imgObj is the newly load image here. Have fun with it now!
})

Categories