Fast Image Loading Methods, Low to High Res with Multiple Backgrounds - JavaScript Solution

Fast Image loading methods, low to high res with multiple backgrounds - javascript solution?

//
// You have dozen of hd photos, and don't want to embed them right from the get go
// in order to avoid 'interlaced' load, boost application load, etc.
// Idea is to place lo-res photos, temporarily, in place where hd ones should go,
// while downloading full quality images in the background.
//
// People usualy do this kind of caching by attaching 'onload' event handler to off-screen
// Image object ( created by new Image(), document.createElement('img'), or any
// other fashion ), which gets executed natively by a browser when the event
// ( 'onload' in this case ) occurs, and setting the '.src' property of an image to
// the phisical path ( relative/absolute ) of an img to start the download process.
// The script pressented here use that approach for multiple images,
// and notifies of task done by running provided function.
//
// So, solution is to provide locations of images you want to,
// and get notified when they get fully downloaded, and cached by browser.
// To do that you pass a function as 1st parameter to the fn below,
// passing as many images as needed after it.
//
// Code will scan through provided images keeping the ones that are actualy
// image files( .jpeg, .png, .tiff, etc.), create 'off-screen' Image objects
// and attach onload/onerror/onabort handler fn to each one( which will be called
// when coresponding circumstance occurs ), and initiate loading by setting the
// .src property of an Image object.
//
// After the 'load-handler' has been called the number of times that coresponds to
// number of images ( meaning the dload process is done ), script notifies you
// of job done by running the function you provided as first argument to it,
// additinaly passing images( that are cached and ready to go ) as
// parameters to callback fn you supplied.
//
// Inside the callback you do whatever you do with cached photos.
//
function hd_pics_dload( fn /* ,...hd-s */ ) {

var
n = 0, // this one is used as counter/flag to signal the download end
P = [], // array to hold Image objects

// here goes the image filtering stuff part,
// all the images that pass the 'regex' test
// ( the ones that have valid image extension )
// are considerd valid, and are kept for download
arg_imgs = Array.prototype.filter.call(
Array.prototype.slice.call( arguments, 1 ),
function ( imgstr ) {
return ( /\.(?:jpe?g|jpe|png|gif|bmp|tiff?|tga|iff)$/i ).test( imgstr );
}
);

// aborts script if no images are provided
// runs passed function anyway

if ( arg_imgs.length === 0 ) {
fn.apply( document, arg_imgs );
return arg_imgs;
}

// this part keeps track of number of 'load-handler' calls,
// when 'n' hits the amount of given photos
// provided callback is runned ( signaling load complete )
// and whatever code is inside of it, it is executed.
// it passes images as parameters to callback,
// and sets it's context ( this ) to document object

var hd_imgs_load_handler = function ( e ) {

// logs the progress to the console
console.log( e.type, ' -- > ', this.src );

( ++n === arg_imgs.length )
&& fn.apply( document, arg_imgs );

};

// this part loops through given images,
// populates the P[] with Image objects,
// attaches 'hd_imgs_load_handler' event handler to each one,
// and starts up the download thing( by setting '.src' to coresponding image path )

for (
var i = 0,
len = arg_imgs.length;
i < len;
i++
) {
P[i] = new Image;
P[i].onabort = hd_imgs_load_handler;
P[i].onerror = hd_imgs_load_handler;
P[i].onload = hd_imgs_load_handler;
P[i].src = arg_imgs[i];
}

// it gives back images that are about to be loaded
return arg_imgs;

}
//
// use:

hd_pics_dload(

// 1st provide a function that will handle photos once cached
function () { console.log('done -> ', arguments ); },

// and provide pics, as many as needed, here
// don't forget to separate all parameters with a comma
'http://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Flag_of_the_United_Nations.svg/2000px-Flag_of_the_United_Nations.svg.png',
'http://upload.wikimedia.org/wikipedia/commons/e/e5/IBM_Port-A-Punch.jpg',
'http://upload.wikimedia.org/wikipedia/commons/7/7e/Tim_Berners-Lee_CP_2.jpg',
'http://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/NewTux.svg/2000px-NewTux.svg.png',
'http://upload.wikimedia.org/wikipedia/commons/4/4c/Beekeeper_keeping_bees.jpg',
'http://upload.wikimedia.org/wikipedia/commons/9/9a/100607-F-1234S-004.jpg'
);

//

Load a low-res background image first, then a high-res one

Here's the method I use...

CSS:

#div_whatever {
position: whatever;
background-repeat: no-repeat;
background-position: whatever whatever;
background-image: url(dir/image.jpg);
/* image.jpg is a low-resolution at 30% quality. */
}

#img_highQuality {
display: none;
}

HTML:

<img id="img_highQuality" src="dir/image.png">
<!-- img.png is a full-resolution image. -->

<div id="div_whatever"></div>

JQUERY:

$("#img_highQuality").off().on("load", function() {
$("#div_whatever").css({
"background-image" : "url(dir/image.png)"
});
});
// Side note: I usually define CSS arrays because
// I inevitably want to go back and add another
// property at some point.

What happens:

  1. A low-res version of the background quickly loads.
  2. Meanwhile, the higher resolution version is loading as a hidden image.
  3. When the high-res image is loaded, jQuery swaps the div's low-res image with the high-res version.

PURE JS VERSION

This example would be efficient for changing one to many elements.

CSS:

.hidden {
display: none;
}

#div_whatever {
position: whatever;
background-repeat: no-repeat;
background-position: whatever whatever;
background-image: url(dir/image.jpg);
/* image.jpg is a low-resolution at 30% quality. */
}

HTML:

<div id="div_whatever"></div>
<img id="img_whatever" class="hidden" src="dir/image.png" onload="upgradeImage(this);">

JAVASCRIPT:

function upgradeImage(object) {
var id = object.id;
var target = "div_" + id.substring(4);

document.getElementById(target).style.backgroundImage = "url(" + object.src + ")";
}

UPDATE / ENHANCEMENT (1/31/2017)

This enhancement is inspired by gdbj's excellent point that my solution results in the image path being specified in three locations. Although I didn't use gdbj's addClass() technique, the following jQuery code is modified to extract the image path (rather than it being hardwired into the jQuery code). More importantly, this version allows for multiple low-res to high-res image substitutions.

CSS

.img_highres {
display: none;
}

#div_whatever1 {
width: 100px;
height: 100px;
background-repeat: no-repeat;
background-position: center center;
background-image: url(PATH_TO_LOW_RES_PHOTO_1);
}

#div_whatever2 {
width: 200px;
height: 200px;
background-repeat: no-repeat;
background-position: center center;
background-image: url(PATH_TO_LOW_RES_PHOTO_2);
}

HTML

<div id="div_whatever1"></div>
<img id="img_whatever1" class="img_highres" src="PATH_TO_HIGH_RES_PHOTO_1">

<div id="div_whatever2"></div>
<img id="img_whatever2" class="img_highres" src="PATH_TO_HIGH_RES_PHOTO_2">

JQUERY

  $(function() {
$(".img_highres").off().on("load", function() {
var id = $(this).attr("id");
var highres = $(this).attr("src").toString();
var target = "#div_" + id.substring(4);
$(target).css("background-image", "url(" + highres + ")");
});
});

What's happens:

  1. Low res images are loaded for each of the divs based on their CSS
    background-image settings. (Note that the CSS also sets the div to the intended
    dimensions.)
  2. Meanwhile, the higher resolution photos are being
    loaded as hidden images (all sharing a class name of img_highres).
  3. A jQuery function is triggered each time an img_highres photo
    completes loading.
  4. The jQuery function reads the image src path, and
    changes the background image of the corresponding div. In the
    example above, the naming convention is "div_[name]" for the visible divs
    and "img_[same name]" for the high res images loaded in the
    background.

How to change Multiple small-res image source with the actual high-res image after they have been loaded?

As the comment suggests, Progressive image loading like medium can be implemented by using a simple open-source files from GitHub blurry-image-load. Read readme.md and add the necessary files to your project and follow the instruction.

Display low resolution image and then high resolution image after few seconds (HTML)

It depends how you generate/create your images as to whether they will be progressive or not. If your images are not progressive, and you have ImageMagick installed (many Linuuxes do) you can convert an image from non-progressive (also known as baseline JPEG) to progressive with this command and try it out on your website:

convert nonProgressive.jpg PJPEG:Progressive.jpg

ImageMagick is available for Windows, OSX, Linux for free from here.

Another way to minimise image sizes is with jhead, and the following command strips out all EXIF information from your image to make it smaller - information removed is things like GPS coordinates, date and time picture was taken, camera model and focal length and shutter speed.

jhead -purejpg image.jpg

Updated Answer

In response to your further question about doing ALL your images, I am not here to tell you what to do! It is your website - you can do as you wish. I was merely suggesting a way for you to try it out on an image and see if you like the results and the performance. If you want to apply to it all your images, it is quite easy, either using standard tools, or GNU Parallel which will do the job in a fraction of the time by using all your CPU cores. Whatever you do, I would urge you to make a backup first in case anything goes wrong or you later decide progressive, EXIF-stripped JPEGs are not for you.

So, after making a backup, you could do one of these options assuming your website is in /var/www:

find /var/www -iname "*.JPG" -exec convert "{}" "PJPEG:{}" \;

or the same again, with EXIF-stripping, and also colour profile, stripping:

find /var/www -iname "*.jpg" -exec convert "{}" -strip "PJPEG:{}" \;

Or you could use GNU Parallel, like this to use all your CPU cores:

find /var/www -iname "*.jpg" | parallel convert "{}" -strip "PJPEG:{}"

How to load a high res background image without impacting large contentful paint on page load

I have become keen on not using "background-image" for my high res background image.

I prefer to have something like :

<style>
.BackgroundImage-Container {
position: relative;
width : 100%;
height : 100%;
}

.BackgroundImage {
position: absolute;
top : 0;
right : 0;
bottom: 0;
left: 0;
width : 100%;
height : 100%;
object-fit: cover;
}
</style>

<picture class="BackgroundImage-Container">
<img class="BackgroundImage" src="./some-image.png" />
</picture>

So I am able to use any lazyload, srcset, <source media="" ></source> that pleases me.

Image loading like google image PHP

Sounds like you want what are called "progressive JPEGs", where multiple images of varying quality are saved in one file, and the browser loads the low resolution images quickly to get something in the page, and replaces it with higher resolution images once they've been loaded.

Here's a tutorial on saving JPEGs for the web as progressive in Photoshop

Making my background images load faster

You shouldn't use .png for such an image. As a general rule, photographs should be .jpg and graphics (eg. logos) should be indexed .png

I reduced the file size by ~93% down to 89KB from 1.3MB and the visual difference is barely noticeable.

Here's the optimized image: Optimized

And here's yours: Original



Related Topics



Leave a reply



Submit