Search code examples
javascriptjquerycssscrollbackground-image

Change background image on pagescroll to create animation? Is canvas more efficient?


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/


Solution

  • 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;
        }
    })