High performance 60 FPS Parallax

Here at Morris we love interactive websites and when done right, advanced techniques like parallax scrolling can really help to create an interesting experience for visitors.

First pioneered by NikeBetterWorld over 3 years ago, the parallax scrolling effect soon became very popular amongst web designers and developers, but it came at a cost. Many implemented such features in a way that their websites now became unbearably slow to scroll, with frame-rates sometimes dropping to below 5 FPS (we don’t want to name and shame so we won’t link to any examples of bad parallax).

So, the initial idea of improving the experience for the user is hindered by it’s own being, the user now has to put up with a slow, jerky experience when navigating the site just for a few fancy animations. In this case, it’d be better to just remove the parallax, no?

Our aim was to find a way how to make parallax run as fast as possible and we discovered the best way to do this is to place the animating elements into their own composite layers.

By moving the parallax elements in a 3d space, the graphics processor on the device is used to hardware-accelerate the transitions. And when the animation effects are taking place, the browser isn’t “painting” them to the screen like usual. The key is to use transform: translate3d.

We’ve seen web pages improve from 15FPS to 60FPS just by changing non-3d transitions to 3d transitions (margin-top and background-position are the main culprits).

Check out the demo below.

Bad Parallax demo Good Parallax demo Download demo as ZIP
The HTML
<div class="image-one">
    <img src="landscape.jpg" data-parallax="true" data-speed="0.4" data-direction="up"> 
    <p data-parallax="true" data-speed="0.1" data-direction="up">Fancy as hell</p>
    <p data-parallax="true" data-speed="0.2" data-direction="up" class="text-two">Parallax scrolling</p>
    <p data-parallax="true" data-speed="0.3" data-direction="up" class="text-three">With love, Morris</p>
</div>
The JavaScript / jQuery
$(document).ready(function(){

    function draw() {
        requestAnimationFrame(draw);
        // Drawing code goes here
        scrollEvent();
    }
    draw();

});

function scrollEvent(){

    if(!is_touch_device()){
        viewportTop = $(window).scrollTop();
        windowHeight = $(window).height();
        viewportBottom = windowHeight+viewportTop;

        if($(window).width())

        $('[data-parallax="true"]').each(function(){
            distance = viewportTop * $(this).attr('data-speed');
            if($(this).attr('data-direction') === 'up'){ sym = '-'; } else { sym = ''; }
            $(this).css('transform','translate3d(0, ' + sym + distance +'px,0)');
        });

    }
}   

function is_touch_device() {
  return 'ontouchstart' in window // works on most browsers 
      || 'onmsgesturechange' in window; // works on ie10
}
The CSS
html,body{margin:0;padding:0;}
body{ background: #232428; color:white; text-align: center; font-family: Arial; font-size:28px; font-weight:bold; letter-spacing: -2px; height:3000px; }
h1{ margin-top:40px;}
div{ width:100%; height:100%; display: inline-block; left:0; top:0%; overflow: hidden; position: fixed; }
img{ width:100%; height:auto; }
p{ position: absolute; width: 100%; top:20%; text-align: center; }
p.text-two{ position: absolute; width: 100%; top:40%; text-align: center;  font-size:15px; letter-spacing: 0;}
p.text-three{ position: absolute; width: 100%; top:60%; text-align: center;  font-size:33px; letter-spacing: 0;}

To test if a parallax element is being painted to the screen continuously or not, try this: in Google Chrome, navigate to any parallax scrolling website, right-click anywhere on the page, click on Inspect Element, once the inspector window is open, click somewhere in it, then press the Esc key on your keyboard to bring up a menu bar, here select the Rendering tab, and tick ‘Show paint rectangles’.  You can test out the FPS of a site/web-app here too.

If a parallax has been implemented badly, you will see the element being painted on the screen continuously. If it has been implemented right, you won’t see any painting being done at all.

Whilst the difference between the two examples we’ve provided is barely noticeable, when we recorded a run with Chrome’s Timeline feature, we saw a vast difference in the amount of work the browser is having to do. Example below.

Easy 2x retina images with jQuery


Read the Tutorial

Our new website finally arrives!


Read the article

Loading