Martin Wedvich
Martin Wedvich

Reputation: 2266

Jerky parallaxing with scrollwheel in WebKit

I'm having some issues with parallaxing backgrounds. I've made a little website for an event organized by some friends of mine, and on that site I have a bunch of background images alternating in between content sections. I've added some logic to offset these background images when scrolling, to create a parallaxing effect.

It's working well enough and I haven't noticed any performance issues, but when using the scrollwheel, the parallaxing seems to be lagging behind in WebKit browsers.

Here's a link to the website:

http://eventyrfestival.info/

The effect I've tried to mimic, at least for the background images, is the one seen on the Spotify website:

https://www.spotify.com/

From looking at their source code, I seem to be doing more or less the same thing: I have a parallaxing function that calculates the background transform based on the scrollTop value of the document, and this function is throttled to 16 ms and bound to the window's scroll event. Still, the background transformation on the Spotify site is instant, while mine visually lags behind the content. It's not "broken" as in it works well in Firefox/IE and it works in WebKit browsers when sliding the scrollbar manually... but it's really annoying.

Does anyone have any tips on what is causing this jerkiness?

Here's the code for the parallaxing functionality (I'm using prototype so sorry about the this spam):

    parallaxBackground: function () {
        var viewportTop = this.elements.$document.scrollTop();
        var viewportBottom = viewportTop + this.elements.$window.height();
        var scrollDelta = this.slideHeight + this.elements.$window.height();

        $.each( this.backgroundSlides, function ( index, slide ) {
            var slideTop = slide.$container.offset().top;
            var slideBottom = slideTop + this.slideHeight;
            if ( slideBottom < viewportTop || slideTop > viewportBottom )
                return true;
            var factor = 1 - ( slideBottom - viewportTop ) / scrollDelta;
            this.transformBackground( slide.$image, this.slideLength * ( factor - 1 ) );
        }.bind( this ) );
    },

    transformBackground: Modernizr.csstransforms ? function ( $backgroundElement, distance ) {
        $backgroundElement.css( {
            '-ms-transform': 'translate(0px, ' + distance + 'px)',
            '-webkit-transform': 'translate(0px, ' + distance + 'px)',
            'transform': 'translate(0px, ' + distance + 'px)'
        } );
    } : function ( $backgroundElement, distance ) {
        $backgroundElement.css( 'top', distance );
    }

And here's how it gets bound (using Underscore for throttling):

this.elements.$window.on( 'scroll',
    _.throttle( this.parallaxBackground.bind( this ), 16 ) );

Upvotes: 5

Views: 525

Answers (1)

mikegreiling
mikegreiling

Reputation: 1161

I recently recreated my own parallax effect based on Spotify's website and I ran across many of the issues you mention here.

While I cannot completely get rid of the stutter on Safari specifically, I've managed to get it to a smooth 60fps on Chrome and Firefox.

I've published it as a jQuery plugin here, but you could adapt it to use your framework of choice:

http://pixelcog.com/parallax.js/

There were several tips which helped me optimize it:

  1. Avoid triggering a layout whenever possible

  2. Don't attach a lot of logic to the scroll event directly, defer to a setTimeout or requestAnimationFrame call instead

  3. Utilize position:fixed; on the background elements. On browsers which lag a bit, the stutter will be minimal.

  4. Enforce browser layering with null transforms, but do so sparingly.

  5. Explore Chrome DevTools to diagnose bottlenecks

These helped me get rid of most of the stutter I was seeing in my own implementation.

Upvotes: 1

Related Questions