4

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 ) );
LittleBobbyTables - Au Revoir
  • 32,008
  • 25
  • 109
  • 114
Martin Wedvich
  • 2,158
  • 2
  • 21
  • 37
  • 1
    This is just a guess, because I had a somewhat similar problem once. Maybe by the time your scroll event fires the window (in webkit) didn't recalculate jet, so you get old values that frame. Try getting the amount you scrolled from the scroll event instead of from the window. Sorry for the vague hint, I used it only in WebGL and not for CSS animations and i'm not super familiar with jQuery. – Winchestro Jun 01 '14 at 16:53
  • As far as I can tell, the only way to get the scrollTop value from a scroll event is to access it through the `target` (or `srcElement`) property of the event object, such as this: `e.target.document.body.scrollTop`. However, the document node would be the same as the one used to call jQuery's `scrollTop()`, so wouldn't I have the same issue there? – Martin Wedvich Jun 02 '14 at 11:40
  • You're right, I think I confused scroll with wheel event where you have a wheel delta value in the event. But looking at the source and step-by-step debugging I realized a call to jQuery's "throttle" (which for some reason happens somewhere) causes everything to happen async with a timeout of 1ms. Maybe this causes the problem? – Winchestro Jun 02 '14 at 16:20
  • Ok I probably just had a similar problem, I solved it by also taking my values from the window rather than from the document. Maybe it works for your case, too. – Winchestro Jun 03 '14 at 11:41
  • Thanks, I'll give that a shot when I get home and let you know how it works out. – Martin Wedvich Jun 03 '14 at 11:47
  • The corresponding value would be `pageYOffset` for the window object, but that made no difference either :( – Martin Wedvich Jun 03 '14 at 16:30
  • Try `window.scrollY` and maybe check out why the 1ms timeout gets called and if you can avoid it by not using jQuery. You can check it by pausing script execution in the inspector and stepping through it (it's easier if you always use non-minified code for debugging btw) – Winchestro Jun 03 '14 at 17:07
  • Same issue with `window.scrollY`, no difference when binding directly to the window instead of jQ either. I tried walking through it with breakpoints, and it seems to just redraw the document with the new scroll location, and only hits the breakpoint after that. Same thing happens in Firefox as well though. When I disable smooth scrolling in FF, I get the same kind of jerking on some scrolls, but not consistently. – Martin Wedvich Jun 03 '14 at 20:12

1 Answers1

1

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.

mikegreiling
  • 1,160
  • 12
  • 21