Spinner

Background Image Cross-Fading with Pre-loader

I wanted to implement an image gallery by cross-fading background images for Planet Diego. I was looking for a solution that would be performant and flexible. Performance is critical since the widget is an essential part of the homepage and the first image needs to be displayed immediately while continuing to load an arbitrary number of images in the background without affecting the rendering speed of the site. Furthermore, I wasn’t too keen on loading heavy weight JavaScript slider libraries with lots of features for a simple effect. Moreover, not too many offer cross-fading effects and don’t leverage hardware accelerated CSS transitions which would perform much smoother on a mobile dominated ecosystem.

Let’s assume we start with the following JSON structure stored at myApp.imageGallery. We have a “fallback” image in case the image array is returned empty and there is no gallery to display. The rotation speed (“rotateEvery”) is expressed in seconds and it is the trigger to advance to the next image. The “base” is simply the base URL for the images array. Lastly, the “images” is an array of filenames to be displayed.

{
    "fallback": "/static/images/home/default.jpg",
    "rotateEvery": 12,
    "base": "http://cdn.planetdiego.com/web/home/",
    "images": [ "image1.jpg", "image2.jpg", "image3.jpg", "image4.jpg", "image5.jpg" ] 
}

The following is the minimal CSS needed for the element holding the background image. Let’s assume for this example: <figure id='main-image'></figure>

#main-image {
    z-index: 1;
    background-size: cover;
    background-position: center center;
    background-blend-mode: darken;
    transition: 3s;
}

Here is the solution I came up with. Once simple validation is passed, “imageSequencer” is invoked a startup image from the array is display. Immediately after, the image preloading mechanism is started, thus caching the upcoming images for a smooth, flicker-free transition. Once caching is complete, setInterval timer is initiated to take over the automatic transition based on the interval specified.

$(function () {
    "use strict";

    var setMainImage = function (url) {
        $('#main-image').css('background-image', 'url(' + url + ')');
    };

    var rotateMainImage = function (args) {
        if (typeof args.images != 'undefined' && args.images != null && args.images.length > 0) {
            console.log('Initiating main image sequencer...');
            imageSequencer(args.base, args.images, args.rotateEvery);
        } else {
            console.log('Setting main image:' + args.fallback);
            setMainImage(args.fallback);
        }
    };

    var preloadImages = function (base, imagesArray, callback) {
        console.log('Caching main images');
        var i,
            j,
            loaded = 0;

        for (i = 0, j = imagesArray.length; i < j; i++) {
            (function (img, src) {
                img.onload = function () {
                    if (++loaded === imagesArray.length && callback) {
                        console.log('Firing image pre-loader callback.');
                        callback();
                    }
                };

                img.src = src;
            }(new Image(), base + imagesArray[i]));
        }
    };

    var imageSequencer = function (base, imagesArray, intervalSecs) {
        console.log('Rotating main image every ' + intervalSecs.toString() + ' seconds');
        var currImage = 0,
            totalImages = imagesArray.length,
            speed = intervalSecs * 1000;

        // Display initial image (last one to prevent repeat on cycle)
        setMainImage(base + imagesArray[totalImages - 1]);

        // Initiate sequencer
        preloadImages(base, imagesArray, function () {

            var siId = setInterval(function () {
                if (currImage >= totalImages) {
                    console.log('Restarting image sequencer.');
                    currImage = 0;
                }

                console.log('Setting main image through sequencer: ' + base + imagesArray[currImage]);
                setMainImage(base + imagesArray[currImage]);
                currImage++;

            }, speed);
        });
    };

    // Initiate Image crossfade
    rotateMainImage(myApp.imageGallery);

});

A couple of important things to note:

  • This solution works well on WebKit based browsers like Safari, Chrome and Opera.
  • Crossfading also works well on mobile devices.
  • On Firefox and Internet Explorer the image will just transition without any effects.
  • Make sure that “rotateEvery” is higher than the CSS Transition value, preferable a multiple of it.
  • For best results, make sure all the images have the same dimensions, or at least the same aspect ratio.
The resulting effect can be seen at http://planetdiego.com/

1 comment:

lpxs10e0b9 said...

For example, 1xBet the state currently receives revenue when individuals spend cash on sure issues, similar to lottery video games or purchasing. This means the state won't obtain new revenue when individuals spend much less on these issues so they might make sports bets. The best on line casino sites are devoted to accountable gaming, so it’s a term you'll typically see when playing on a good online on line casino site within the UK. Responsible gaming refers to how the participant interacts with the site and what the site does guarantee that|to make certain that} guidelines are being adopted. Much of the actual duty lies with the players, who must comply with particular guidelines to play responsibly.