15

I've been looking around a lot of JavaScript answers but I haven't found one that really answers my problem yet. What I'm trying to do is load an image, grab the pixel data, perform an analysis, and then load another image to repeat the process.

My problem is that I can't preload all of the images because this script has to be able to work on large amounts of images and preloading could be too resource heavy. So I'm stuck trying to load a new image each time through a loop, but I'm stuck with a race condition between the image loading and the script drawing it to the canvas's context. At least I'm pretty sure that's what is happening because the script will work fine with the images precached (for example if I refresh after loading the page previously).

As you'll see there are several lines of code commented out because I'm incredibly new to JavaScript and they weren't working the way I thought they would, but I didn't want to forget about them if I needed the functionality later.

This is the snippet of code that I believe is giving rise to the problem:

EDIT: So I got my function to work after following a suggestion

 function myFunction(imageURLarray) {
  var canvas = document.getElementById('imagecanvas');
  console.log("Canvas Grabbed");

  if (!canvas || !canvas.getContext) {
    return;
  }

  var context = canvas.getContext('2d');

  if (!context || !context.putImageData) {
    return;
  }

  window.loadedImageCount = 0;
  loadImages(context, canvas.width, canvas.height, imageURLarray, 0);
}

function loadImages(context, width, height, imageURLarray, currentIndex) {
  if (imageURLarray.length == 0 || imageURLarray.length == currentIndex) {
    return false;
  }
  if (typeof currentIndex == 'undefined') {
    currentIndex = 0;
  }

  var currentimage = new Image();
  currentimage.src = imageURLarray[currentIndex];

  var tempindex = currentIndex;
  currentimage.onload = function(e) {

    // Function code here

    window.loadedImageCount++;

    if (loadedImageCount == imageURLarray.length) {

      // Function that happens after all images are loaded here
    }
  }
  currentIndex++;
  loadImages(context, width, height, imageURLarray, currentIndex);
  return;
}
Braains
  • 606
  • 1
  • 5
  • 22
  • This question still gets views 4 years later so I thought I mention that if you have a lot of images you may want to call the recursive `loadImages` with `setTimeout(..., 0)` so that the call stack doesn't overflow. – Braains Jul 25 '17 at 20:03

3 Answers3

14

Maybe this will help:

currentimage.onload = function(e){
    // code, run after image load
}

If it is necessary to wait for the image to load, the following code will load the next image (currentIndex is your "img" variable):

var loadImages = function(imageURLarray, currentIndex){
    if (imageURLarray.length == 0 || imageURLarray.length == currentIndex) return false;
    if (typeof currentIndex == 'undefined'){
       currentIndex = 0;
    }
    // your top code
    currentimage.onload = function(e){
        // code, run after image load
        loadImages(imageURLArray, currentIndex++);
    }
}

Instead of a "for" loop, use for example this function:

loadImages(imageURLarray);
Neptilo
  • 465
  • 1
  • 5
  • 17
maximkou
  • 5,252
  • 1
  • 20
  • 41
  • 2
    I was looking at this but my understanding is that the rest of the script will continue to run until the image loads and that function is triggered, and so I'll have the same race condition. – Braains Jun 04 '13 at 14:17
  • @Braains, If properly implemented, then no. If you have another code, show it – maximkou Jun 04 '13 at 14:21
  • 1
    onload won't wait to load next images – A. Wolff Jun 04 '13 at 14:23
  • Give me a few minutes here to implement this and see if it works! But I'm inclined to agree with @roasted – Braains Jun 04 '13 at 14:24
  • "@Braains, What I see now: you only need all the code that begin with console.log('Image drawn') insert in callback. – maximkou Jun 04 '13 at 14:24
  • @maximkou can you give me an example of what you mean? I don't see how a callback stops JavaScript from running without the image being fully loaded. – Braains Jun 04 '13 at 14:30
  • That could work! Let me implement it, I'll be back with results in a few. – Braains Jun 04 '13 at 14:34
  • 3
    @Braains but this is basically my answer :) You have to set src of image after setting onload handler otherwise onload event could be not called on cached image (browser behaviour) – A. Wolff Jun 04 '13 at 14:37
  • @maximkou, I tried your solution, but the images don't load in the order they come in so it can move on before the every image is loaded. – Braains Jun 04 '13 at 15:36
  • @maximkou is there a way for me to mark this question as answered? I updated the original post to show my changed code in case future googlers want to see my solution. – Braains Jun 05 '13 at 15:28
  • @Braains, without selecting an answer? If yes, then I do not know. – maximkou Jun 05 '13 at 16:48
  • @maximkou I figured it out! – Braains Jun 06 '13 at 18:59
0

Maybe try this:

http://jsfiddle.net/jnt9f/

Setting onload handler before setting img src will make sure the onload event be fired even the image is cached

var $imgs = $(),i=0;
for (var img = 0; img < imageURLarray.length; img++) {
   $imgs = $imgs.add('<img/>');
}

var fctn = (function fctn(i){
    $imgs.eq(i).on('load',function(){
        //do some stuff
        //...
        fctn(++i);
    }).attr('src',imageURLarray[i]);    
})(0);
A. Wolff
  • 74,033
  • 9
  • 94
  • 155
  • Could you maybe explain a little of what is happening in your example code? I'm not as familiar with JQuery as I should be. – Braains Jun 04 '13 at 14:17
  • This is to avoid any race condition. You will start loading next image only when previous as fully loaded. See jsfiddle in updated answer – A. Wolff Jun 04 '13 at 14:22
  • After I try implementing the other answer I will try this. I'm a little more familiar with recursion than JQuery so I want to try that first. – Braains Jun 04 '13 at 15:00
0

Actually...a lot of developers are pointing here to detect when images are done loading after a jQuery event..

https://github.com/desandro/imagesloaded

If you can determine when the event triggers your images to load (for example, adding an Id or class onto the page right before your images begin to load), then you should be able to blend that in with this plug-in on github.

Good Luck!

klewis
  • 7,459
  • 15
  • 58
  • 102
  • Tried that, but I'm not just loading one image, I'm loading an image each time through the loop. All of this happens after the page loads. – Braains Jun 04 '13 at 14:15
  • The problem with preloading is that I'm dealing with a potentially large number of images. – Braains Jun 04 '13 at 14:32
  • whats triggering these "additional" images to load? – klewis Jun 04 '13 at 14:41
  • 1
    my script is loading an image each time through the loop with var currentimage = new Image(); currentimage.src = imageURLarray[img]; – Braains Jun 04 '13 at 14:59