6

When an image object is created, can know when is fully loaded using the "complete" property, or the "onload" method, then, this image has processed ( resizing for example ) using some time, that can be some seconds in big files.
How to know when browser finish to process an image after loading it?

EDIT: In examples can see a lag between "complete" message and the appearance of the image, I want avoid this.

Example ussing "onload" method:

var BIGimage;
 putBIGimage();
 function putBIGimage(){
  BIGimage=document.createElement("IMG");
  BIGimage.height=200;
  BIGimage.src="http://orig09.deviantart.net/5e53/f/2013/347/f/d/i_don_t_want_to_ever_leave_the_lake_district_by_martinkantauskas-d6xrdch.jpg";
  BIGimage.onload=function(){waitBIGimage();};
 }
 function waitBIGimage(){
   console.log("Complete.");
   document.body.appendChild(BIGimage);
 }

Example using "complete" property:

var BIGimage;
 putBIGimage();
 function putBIGimage(){
  BIGimage=document.createElement("IMG");
  BIGimage.height=200;
  BIGimage.src="http://orig09.deviantart.net/5e53/f/2013/347/f/d/i_don_t_want_to_ever_leave_the_lake_district_by_martinkantauskas-d6xrdch.jpg";
  waitBIGimage();
 }
 function waitBIGimage(){
  if (!BIGimage.complete){
   console.log("Loading...");
   setTimeout(function(){
    waitBIGimage();
   },16);
  } else {
   console.log("Complete.");
   document.body.appendChild(BIGimage);
  }
 }

EDIT: Thanks the @Kaiido's response I made this sollution for wait the images process.

var imagesList=["https://omastewitkowski.files.wordpress.com/2013/07/howard-prairie-lake-oregon-omaste-witkowski-owfotografik-com-2-2.jpg",
  "http://orig03.deviantart.net/7b8d/f/2015/289/0/f/0ffd635880709fb39c2b69f782de9663-d9d9w6l.jpg",
  "http://www.livandiz.com/dpr/Crater%20Lake%20Pano%2016799x5507.JPG"];
var BIGimages=loadImages(imagesList);
onLoadImages(BIGimages,showImages);

function loadImages(listImages){
 var image;
 var list=[];
 for (var i=0;i<listImages.length;i++){
  image=document.createElement("IMG");
  image.height=200;
  image.src=listImages[i]+"?"+Math.random();
  list.push(image);
 }
 return list;  
}

function showImages(){
 loading.style.display="none";
 for (var i=0; i<BIGimages.length;i++){
  document.body.appendChild(BIGimages[i]);
 }
};

function onLoadImages(images,callBack,n) {
 if (images==undefined) return null;
 if (callBack==undefined) callBack=function(){};
 else if (typeof callBack!="function") return null;
 var list=[];
 if (!Array.isArray(images)){
  if (typeof images =="string") images=document.getElementById(images);
  if (!images || images.tagName!="IMG") return null;
  list.push(images);
 } else list=images;
 if (n==undefined || n<0 || n>=list.length) n=0;
 for (var i=n; i<list.length; i++){
  if (!list[i].complete){
   setTimeout(function(){onLoadImages(images,callBack,i);},16);
   return false;
  }
  var ctx = document.createElement('canvas').getContext('2d');
  ctx.drawImage(list[i], 0, 0);
 }
 callBack();
 return true;
}
<DIV id="loading">Loading some big images...</DIV>
Arnau Castellví
  • 240
  • 2
  • 11

2 Answers2

4

The HTMLImageElement interface has a decode() method, which does allow us to wait until the image is ready to be drawn, just like you want.

var BIGimage;
putBIGimage();

function putBIGimage() {
  BIGimage = document.createElement("IMG");
  BIGimage.height = 200;
  BIGimage.src = "https://upload.wikimedia.org/wikipedia/commons/c/cf/Black_hole_-_Messier_87.jpg?r=" + Math.random();
  BIGimage.onload = e => console.log('load event', performance.now());
  BIGimage.decode().then(waitBIGimage);
  BIGimage.onerror = console.error;
}

function waitBIGimage() {
  // uses the synchronous testing method to check it works fine
  var start = performance.now();
  // only to see if it worked fine
  var ctx = document.createElement('canvas').getContext('2d');
  ctx.drawImage(BIGimage, 0, 0);
  ctx.drawImage(ctx.canvas, 0, 0);

  var end = performance.now();
  console.log(`it took ${end - start}ms to draw`)

  // do your stuff
  document.body.appendChild(BIGimage);
}

Another way, is to use the fact that the CanvasContext2D drawImage method can be synchronous. Some browsers may try to delay the actual painting for the next painting frame, but by drawing the canvas onto itself we can force most of the current UAs to render our image synchronously.

So you can use it as a waiting method in your waitBIGimage method.

var BIGimage;
putBIGimage();

function putBIGimage() {
  BIGimage = document.createElement("IMG");
  BIGimage.height = 200;
  BIGimage.src = "https://upload.wikimedia.org/wikipedia/commons/c/cf/Black_hole_-_Messier_87.jpg?r=" + Math.random();
  BIGimage.onload = waitBIGimage;
  BIGimage.onerror = console.error;
}

function waitBIGimage() {
  // only for demo
  // we've got to also log the time since even the console.log method will be blocked during processing
  var start = performance.now();
  console.log('waiting', start);

  // this is all needed
  var ctx = document.createElement('canvas').getContext('2d');
  ctx.drawImage(BIGimage, 0, 0);
  // on some system the drawing of the image may be delayed to the next painting frame
  // we can try force the synchronous rendering by drawing the canvas over itself
  ctx.drawImage(ctx.canvas, 0, 0);
  // demo only
  var end = performance.now();
  console.log("Complete.", end);
  console.log(`it took ${end - start}ms`)

  // do your stuff
  document.body.appendChild(BIGimage);
}

On my Firefox it takes about 1 second to process the image, while on my Chrome, it's a bit faster.

But one big issue with method is that it is synchronous, and thus will block your scripts during all the time the Image is processed.


Yet another way, is to use the createImageBitmap() method, which should allocate the image bitmap in the GPU, ready to be painted.

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Thanks, I edit my question with a sollution that I made :) – Arnau Castellví Sep 17 '16 at 17:23
  • 1
    @ArnauCastellví glad it helped you but you should not rely on the complete status like that. First, you'll make a lot of useless call to your function, while js gets its strength from the Event model. Also, complete may be set to true if the image failed to load, but you can't determine it from this property (though you could check for naturalWidth property). What you want to achieve should be done by listening to all your images onload and onerror event, incrementing a counter variable, and triggering the final callback only when the counter reaches the total number of images to load. – Kaiido Sep 18 '16 at 04:26
  • Well, yes, is only a test of concept, i'm still working in it. Thank you :) – Arnau Castellví Sep 18 '16 at 04:35
2

There is no reliable way to know - your browser can continue to execute arbitrary javascript or perform built in functions (i.e., resizing of the browser window) which can directly or indirectly affect the image and cause it to be either redrawn, or appear to not finish drawing for a while.

You can hook in to particular events in the lifetime of an image, but, strictly speaking, "finish" only happens when the browser window is closed.

blueberryfields
  • 45,910
  • 28
  • 89
  • 168