1

I am working on a parallax site in which there are sequence of images (around 400 images). The background images change based on page scroll to create a smoothly moving animation. I managed to get the scrolling working, but when the user scrolls, the change of background images are not smooth (We can see the blank space for a second or so depending on the internet connection). Also, the images are not being cached, the page does a new request every time. How can I optimize this code so that the animation is smooth and it doesn't request a new image every time and uses the cached images. Is it efficient to create the animation in canvas? I tried canvas, but it also makes a new request to images on every scroll. Here is my code using standard background changing based on page scroll:

HTML

<div class="container">
    <div id="background-images" class="background-images">
        <div class="test"></div>
    </div>
</div>

CSS

#background-images{
    height: 4000px;
}
.container{
    border: 1px solid red;
    height: 400px;
    overflow: auto;
}
.test{
    position: fixed;
    top: 0;
    left: 0;
    z-index: 999;
    width: 600px;
    height: 600px;
}

Javascript

var $container = $(".container");
var $bgImage = $(".test");

// Attaching the scroll to the background image
$container.scroll(function(event) {
    var position = $container.scrollTop();
    setBgImage(position);
});

// preload the given total amount of iamges
function preload(totalImages) {
    for (var i = 0; i < totalImages; i++) {
        $('<img/>')[0].src = getImgUrl(i);
    }
}
preload(36); // Preload 36 images, the cache should keep these so we wont't need to load these while we scroll

// Set the background image
function setBgImage(position){
    var imageNum;
    var lineCount = 0;

    imageNum = parseInt(position  / 100);

    console.log("IMG: " + imageNum + ", Position: " + position);
    $bgImage.css("background-image", "url('" + getImgUrl(imageNum) + "')");
}
// Set a placeholder background image
function getImgUrl(num){
    return "http://placehold.it/200x200/&text=" + num;  
}

JSFiddle: http://jsfiddle.net/4j9u8qtf/1/

Suthan Bala
  • 3,209
  • 5
  • 34
  • 59
  • Can you open firebug or chrome dev tools and verify that your resources are being loaded (network tab) by the preload as expected? – Brad Wells Sep 22 '14 at 18:13
  • Yes it does. It loads everything with status code of 200, then everytime the page scrolls it loads them again with status 200 – Suthan Bala Sep 22 '14 at 18:15
  • See [this excellent answer](http://stackoverflow.com/questions/1373142/preloading-css-background-images) for an idea how to preload background images. – fstanis Sep 22 '14 at 18:21
  • Probably irrelevant, but do you have the console open when you test this? Having the console open can slow things down quite a bit, especially if you're consoling something out (which you are, on every scroll event) – anson Sep 24 '14 at 19:21
  • I tried removing the console logs and having the consoles closed, the same issue persists. The issue is merely the delay is because of the time for images to be received. – Suthan Bala Sep 24 '14 at 19:39
  • Have you try to use a plugin instead of create your own code. I use http://vegas.jaysalvat.com/ to recreate this effect – Jean Oct 01 '14 at 14:01
  • The vegas plugin is completely a different concept – Suthan Bala Oct 01 '14 at 15:33

5 Answers5

3

You could add all the images hidden and just show the correct one in an actual image element, instead of using css background image. I edited your jsfiddle to demonstrate:

function create(totalImages) {
    for (var i = 0; i < totalImages; i++) {
        var img = $('<img/>')
        img[0].src = getImgUrl(i);
        $bgImage.append(img)
    }
    setBgImage(0)
}

create(37);

function setBgImage(position){
    var imageNum;
    var lineCount = 0;

    imageNum = parseInt(position  / 100);

    console.log("IMG: " + imageNum + ", Position: " + position);

    $bgImage.find("img").hide().eq(imageNum).show()
}

http://jsfiddle.net/y92g7vvL/1/

OlliM
  • 7,023
  • 1
  • 36
  • 47
  • Looks good, but I'm not sure how fast the jQuery is going to be when updating the images while scrolling. I'll keep you posted on how it goes. Thank you – Suthan Bala Sep 30 '14 at 14:49
  • Updating the images should not be a problem. If you have a lot of images, you'll have to load them on demand, here we load all images at startup, which would be too much if they are large and there are too many of them. – OlliM Oct 01 '14 at 11:54
2

The best way to preload images is to use the Image constructor like the example below. Using the Image constructor makes it so you don't have to worry about attaching the images anywhere to the document to make them load.

function preload(url) {
  var image = new Image();
  image.src = url;
}

I updated your Fiddle to use this preload and to not use jQuery for setting the background-image. It works quite well now. All images are preloaded/loaded only once.

$(function () {
    var $container = $(".container");
    var $bgImage = $(".test");
    var bgImage = $bgImage.get(0);

    $container.scroll(function (event) {
        var position = $container.scrollTop();
        setBgImage(position);
    });

    // preload the given total amount of iamges
    function preload(totalImages) {
        function fetch(url) {
          var image = new Image();
          image.src = url;
        }

        for (var i = 0; i < totalImages; i++) {
            fetch(getImgUrl(i));
        }
    }

    preload(36);

    function setBgImage(position) {
        var imageNum;
        var lineCount = 0;

        imageNum = parseInt(position / 100);
        var url = getImgUrl(imageNum);

        bgImage.style.backgroundImage = "url('"+ url +"')";
    }


    function getImgUrl(num) {
        return "http://placehold.it/200x200/&text=" + num;
    }
})
pseudosavant
  • 7,056
  • 2
  • 36
  • 41
1

Try

html

<div class="container">
    <div id="background-images" class="background-images">
        <div class="test"></div>
    </div>
</div>
<!-- hidden container for `img` elements -->
<div id="imgs"></div>

css

#imgs {
  display:none;
}

js

$(function () {
    var $container = $(".container");
    var $bgImage = $(".test");

    $container.scroll(function (event) {
        var position = $container.scrollTop();
        setBgImage(position);
    });

    // preload the given total amount of iamges
    function preload(totalImages) {
        for (var i = 0; i < totalImages; i++) {
            $('<img/>', {
                "src": getImgUrl(i)
            })
            // append `img` elements to `#imgs` container
            // to `cache` the images , 
            // images not requested again at 
            // `$bgImage` `background-image` adjustments
            .appendTo("#imgs");

        }
    }

    preload(36);

    function setBgImage(position) {
        var imageNum;
        var lineCount = 0;

        imageNum = parseInt(position / 100);

        console.log("IMG: " + imageNum + "
                   , Position: " + position
                   , $("#imgs img[src$=" + imageNum + "]"));

        // utilize images already in DOM ,  
        // load `#imgs img` `src` as `$bgImage` `background-image` , 
        // from hidden `#imgs` `div`
        // images _not_ requested again from server , 
        // see `network` tab at console 
        $bgImage.css("background-image"
        , "url('" + $("#imgs img[src$=" + imageNum + "]").attr("src") + "')");
    }


    function getImgUrl(num) {
        return "http://placehold.it/200x200/&text=" + num;
    }
});

jsfiddle http://jsfiddle.net/guest271314/bh91g0tv/

guest271314
  • 1
  • 15
  • 104
  • 177
0

The reason that they're being requested over and over is that you're using jQuery background assignment, which reloads the image.

This is silly. You don't need or want jQuery here, and it has side effects you don't understand that are causing your problem.

Just set the X position of the background. Done.

function offset_to(id, posX, posY) {
    document.getElementById(id).style.backgroundPosition = posX.toString() + 'px ' + posY.toString() + 'px';
}
John Haugeland
  • 9,230
  • 3
  • 37
  • 40
0

To create a smooth effect to your image transition just add css transition in your case:-

.test{
    -webkit-transition: all 0.5s ease;
    -moz-transition: position 10s;
    -ms-transition: position 10s;
    -o-transition: position 10s;
    transition: all 0.8s ease;
}
Akashxolotl
  • 428
  • 5
  • 6