Create URL to unloaded mock-image in browser to simulate actual loading

175 Views Asked by At

In short, I would like to create a lazy-loaded-mock-image-src-url in browser. This is in order to simulate the loading of an image, to see how it loads.

Here is how I would like the flow to work, all done in-browser.

  1. Create a URL using URL.createObjectURL, canvas.toDataURL or similar, where the actual image that the returned url refers to, is not loaded yet (just like a real scenario).
  2. Pass the returned url that refers to the unloaded image to an html-img-tag.
  3. The image does not (visibly) load in the html-img-tag.
  4. Trigger a load of the image (that the url refers to) (preferably using some JS function) after some delay.
  5. The actual image is (visibly) shown in the html-img-tag.

I have managed to create a src string in the browser that refers to an image, that is passed to an html-img-tag, BUT the image loads instantly.

const canvas = document.createElement('canvas');
// ...create the image using getContext, fillRect etc...
const url = canvas.toDataURL('image/png');

How can I make sure the image that the url refers to is not loaded initially?

Is it better to use URL.createObjectURL or some other method instead?

Is it at all possible?


PS. I don't want to edit the html-img-tag, using onload method as the img is a 3rd party (react) component.

2

There are 2 best solutions below

5
Florent Akpro On

You can try to progressively pass chunk of the url data to the img tag src attributes.

const canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 200;
const ctx = canvas.getContext('2d');

// Draw a blue rectangle
ctx.fillStyle = 'blue';
ctx.fillRect(50, 50, 100, 200);

// Draw a red rectangle
ctx.fillStyle = 'red';
ctx.fillRect(200, 50, 100, 200);


// Convert the canvas to a data URL
const url = canvas.toDataURL('image/png');


// Now use 'url' to progresively pass chunck of data
// to img.src

let inter = null;
let counter = 4;

function slowlyLoadImg(img, segment, time) {
    counter = 4;
    inter = setInterval(() => {
    img.src = url.slice(0, url.length / counter);
    counter -= segment;
    if(counter < 0){
        img.src = url;
         clearInterval(inter);
    }
}, time);
}

window.onload = function() {
    const img = document.querySelector('img');
    slowlyLoadImg(img, 1, 800);
}
2
Helder Sepulveda On

With modern internet speeds images load in milliseconds, to simulate the loading of an image one possibility is to "partially" draw the image in our canvas, and of course with a canvas we can add a lot of fancy animations, imagination is the limit...


Here is a simple left to right animation we can use to simulate the loading of an image.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var x = 0

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.beginPath()
  ctx.drawImage(image, 0, 0, x, image.height, 0, 0, x, image.height);
  x = x + 1
  if (x < image.width) {
    ctx.setLineDash([5, x%10+5]);
    ctx.rect(x, 0, 1, canvas.height)
    ctx.stroke()
    setTimeout(draw, 50)
  }
}

var image = new Image();
image.src = "http://i.stack.imgur.com/UFBxY.png";
image.onload = draw;
<canvas id="canvas" width=500></canvas>


Here is another simple animation using blur

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var x = 50

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.beginPath()
  ctx.filter = 'blur(' + x + 'px)'
  ctx.drawImage(image, 0, 0, image.width, image.height);
  x = x - 0.5
  if (x >= 0) {
    setTimeout(draw, 50)
  }
}

var image = new Image();
image.src = "http://i.stack.imgur.com/UFBxY.png";
image.onload = draw;
<canvas id="canvas" width=500></canvas>


You can use the canvas directly as your image instead of an html-img-tag but if you really want to use an image <img src="." > the code is simple...
after we draw in the canvas we select and add the output of toDataURL

  var img = document.querySelector('img')
  img.src = canvas.toDataURL('image/png')

Just keep in mind that with toDataURL you are likely to run into the CORS issues:

As soon as you draw into a canvas any data that was loaded from another origin without CORS approval, the canvas becomes tainted. A tainted canvas is one which is no longer considered secure, and any attempts to retrieve image data back from the canvas will cause an exception to be thrown.

You can read more about it:
https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image#security_and_tainted_canvases


... and one more way I just remember, is to create the animation with CSS, code is decoupled from the image, and same can be easily applied to many images.

.slow { animation: slow 10s; }
@keyframes slow {
    0% { opacity: 0; }
    100% { opacity: 1; }
}
<img src="http://i.stack.imgur.com/UFBxY.png" class="slow" width="250">