0

I'm trying to preload one image using PreloadJS, then insert it to DOM a twice. But I can insert only one image.

var queue = new createjs.LoadQueue();
queue.addEventListener('complete', onComplete);
queue.loadManifest([{
  id: 'img',
  type: 'image',
  src: 'https://i.imgur.com/adSrF00g.png',
}, ]);
queue.load();

function onComplete(event) {
  var img = queue.getResult('img');
  var imgSrc = img.src;

  // this code inserts image to the DOM but 404 not found error appears in console
  document.getElementById('game').innerHTML = `<img src="${imgSrc}" />`;

  // this code do the same thing and 404 also appears
  var DOM_img = document.createElement("img");
  DOM_img.src = imgSrc;
  document.getElementById('game').appendChild(DOM_img);

  // this code insert the image to the DOM succesfully
  document.getElementById('game').appendChild(img);

  // but the last line does nothing - the img tag does not appear in the DOM and nothing error in the console
  document.getElementById('game').appendChild(img);
}
<script src="https://code.createjs.com/1.0.0/preloadjs.min.js"></script>
<div id="game"></div>

Screenshot: https://i.stack.imgur.com/Um1zW.jpg

I cannot understand why only one image appears. What I'am doing wrong? And how can I insert an image a twice? Thanks!

Teocci
  • 7,189
  • 1
  • 50
  • 48
Blackster
  • 343
  • 2
  • 5

2 Answers2

2

This is easy to fix, but first I want to explain you something.

What are Blob URLs or Object-URLs? Blob URL/Object URL are a pseudo protocol to allow Blob and File objects to be used as URL source for things like images, download links for binary data and so forth.

In your case, If you try to open this src="blob:https://yada.yada" blob url that is the src of a loaded image it will give you an error and it can't be opened.

Yeah, I know that Teo, but Why if is working with the src tag how it is possible?

Well, Blob URLs can only be generated internally by the browser. So those elements have a special reference to the Blob or File object. These URLs can only be used locally in the single instance of the browser and in the same session (i.e. the life of the page/document).

So in this case the src="blob:https://yada.yada" is linked to the img tag created by the LoadQueue object.

Then, how can I use the same image in multiples elements?

In the LoadQueue docummentation that we can get a Blob object using the getResult() method. So, instead to reuse the Blob URL we can "clone" the Blob object. According to the documentation the getResult (value, [rawResult=false]) methods was overwritten to receive this rawResult optional parameter. If this parameter is true the method will returns a raw result as a Blob object, instead of a formatted result. If there is no raw result, the formatted result will be returned instead.

OK, but how can I use this blob in a <img> element?

Well, we can use URL.createObjectURL() method. This method creates a DOMString containing a new Blob URL representing the Blob object given in the parameter.

As I explained above, this Blob URL lifetime is tied to the document in the window on which it was created.

Here is the implementation:

let queue = new createjs.LoadQueue();
queue.addEventListener('complete', onComplete);
queue.loadManifest([{
  id: 'sprite',
  type: 'image',
  src: 'https://i.imgur.com/ciIyRtu.jpg?1',
}, ]);
queue.load();

function onComplete(event) {
  // Get Blob object instead of a formatted result
  let blob = queue.getResult('sprite', true);

  // Create a Blob URL using the Blob object 
  let urls = [
    URL.createObjectURL(blob),
    URL.createObjectURL(blob),
    URL.createObjectURL(blob),
  ];

  // Create a new <img> element and assign a Blob URL
  urls.forEach(function(item, index, array) {
    let DOM_img = document.createElement('img');
    DOM_img.src = item;
    document.getElementById('game').appendChild(DOM_img);
  });
}
<script src="https://code.createjs.com/1.0.0/preloadjs.min.js"></script>
<div id="game"></div>

Alternative solution (Not recommended for big files)

Also you can use the loadManifest() to load the same image multiple times with different ids. Then we define a fileload event instead of complete event to capture each loaded element, check this example:

let queue = new createjs.LoadQueue();
queue.addEventListener('fileload', onFileLoad);
queue.loadManifest([{
  id: 'sprite1',
  type: 'image',
  src: 'https://i.imgur.com/ciIyRtu.jpg?1',
}, {
  id: 'sprite2',
  type: 'image',
  src: 'https://i.imgur.com/ciIyRtu.jpg?1',
}, {
  id: 'sprite3',
  type: 'image',
  src: 'https://i.imgur.com/ciIyRtu.jpg?1',
}, ]);

function onFileLoad(event) {
  // Get the image element after is successfully loaded
  let img = event.result;

  // This code insert the image to the DOM succesfully
  document.getElementById('game').appendChild(img);
}
<script src="https://code.createjs.com/1.0.0/preloadjs.min.js"></script>
<div id="game"></div>
Teocci
  • 7,189
  • 1
  • 50
  • 48
  • Thanks! This works! But for educational puspose, why it is not possible to add an blob image by changing img's src attribute? It appears in the dom with with 404 error. It is browser bug or what? – Blackster Jun 12 '19 at 07:06
  • I explained to you why it is not available. `LoadQueue()` download the image in a cache file that is only available for the element created by event `fileload` this event returns the `result` witch is linked to the ***Blob URL***. – Teocci Jun 12 '19 at 07:41
  • I just noticed that equal img sources with different ids in manifest cause to double loading. This is unacceptable when we need to load one large image and insert him a twice in the DOM. So I'll trying to find another solution for this – Blackster Jun 12 '19 at 21:14
0

Just found another solution that loads image once and then insert it to dom a twice.

var queue = new createjs.LoadQueue();
queue.addEventListener('complete', onComplete);
queue.loadManifest([{
  id: 'img',
  type: 'image',
  src: 'https://i.imgur.com/adSrF00g.png',
}, ]);
queue.load();

function onComplete(event) {
    // get loading results as blob (second argument is set to true)
    var blob = queue.getResult('img', true);
    
    // create two blob urls from one blob image source
    var blobUrl = URL.createObjectURL(blob);
    var blobUrl2 = URL.createObjectURL(blob);
    
    // create new IMG tag and set src as first blob url
    var DOM_img = document.createElement("img");
    DOM_img.src = blobUrl;
    document.getElementById('game').appendChild(DOM_img);

    // create new IMG tag and set src as second blob url
    var DOM_img2 = document.createElement("img");
    DOM_img2.src = blobUrl2;
    document.getElementById('game').appendChild(DOM_img2);
}
<script src="https://code.createjs.com/1.0.0/preloadjs.min.js"></script>
<div id="game"></div>
Blackster
  • 343
  • 2
  • 5