javascripthtmlcreatejspreloadjs

PreloadJS loaded image, but I cannot insert it to DOM


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.sstatic.net/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!


Solution

  • 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>