5
\$\begingroup\$

I have the following script to create a parallax effect on the page using request animation frame, it runs well on Chrome, but on Safari is lagging, I think because I run too many calculations on each rAF.

  $('.parallax').each(function () {
    var section = $(this),
        elements = section.find('[data-parallax-ratio]');

    elements.data('parallax-current', 0);

    function animation() {
      var scrollPos = $(window).scrollTop(),
          offsetY = section.offset().top - 60;

      for (var i = 0; i < elements.length; i++) {
        var el = $(elements[i]),
        ratio = el.data('parallax-ratio'),
        current = el.data('parallax-current');

        var newPos = (offsetY - scrollPos) * ratio;

        var pos = current - ((current - newPos) * 0.08);

        el.css('transform', 'translateY(' + pos + 'px)');

        el.data('parallax-current', pos);
      }

      window.requestAnimationFrame(animation);
    }

    animation();

  });
\$\endgroup\$
6
\$\begingroup\$

The major issue that I see in your code is that you keep requestAnimationFrame no matter what. This obviously causes the browser to update the css of the element at any time.

Since your animation is based on scroll I would suggest to define an handler to window.onscroll.

While doing this you perhaps may wish to transform your animation method to be a plugin method that you would call for every parallax element.

Resulting code becomes something like this:

$.fn.parallaxAnimate = function() {
    var elements = this.find('[data-parallax-ratio]');

    var scrollPos = $(window).scrollTop(),
      offsetY = this.offset().top - 60;

    for (var i = 0; i < elements.length; i++) {
        var el = $(elements[i]),
            ratio = el.data('parallax-ratio'),
            current = el.data('parallax-current') || 0;

        var newPos = (offsetY - scrollPos) * ratio;
        var pos = current - ((current - newPos) * 0.08);

        el.css('transform', 'translateY(' + pos + 'px)');
        el.data('parallax-current', pos);
    }
}

window.onscroll = function(){ $('.parallax').parallaxAnimate(); };

See a (ugly) fiddle

Edit: I was wrong on my previous attempt to solve smoothness and I was also wrong about subscribing onscroll

I tried to solve the problem that the OP is having as the animation is not smooth. On my previous attempt I tried to implement a scroll animation on a scroll event, and I got a warning on firefox (this is a free translation since I got my browser on portuguese unfortunately):

This site appears to use a scroll-linked position effect. This may not work properly with asynchronous panning. See https://developers.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for more details...

And there you can read an explanation of what are the causes of this warning.

Meanwhile, on this same documentation page on Other effects, you can have a link to a useful site that implements many scroll effects, one of those being the parallax. All of this only with css...

I will post here one smaller example based on the implementation of the site, give props to that man please, not to me ;).

<style>

/* Parallax base styles
--------------------------------------------- */
.parallax {
    height: 500px; /* fallback for older browsers */
    height: 100vh;
    overflow-x: hidden;
    overflow-y: auto;
    -webkit-perspective: 300px;
    perspective: 300px;
}

.parallax__group {
    position: relative;
    height: 500px; /* fallback for older browsers */
    height: 100vh;
    -webkit-transform-style: preserve-3d;
    transform-style: preserve-3d;
}

.parallax__layer {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
}

.parallax__layer--fore {
    -webkit-transform: translateZ(90px) scale(.7);
    transform: translateZ(90px) scale(.7);
    z-index: 1;
}

.parallax__layer--base {
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
    z-index: 4;
}

.parallax__layer--back {
    -webkit-transform: translateZ(-300px) scale(2);
    transform: translateZ(-300px) scale(2);
    z-index: 3;
}

.parallax__layer--deep {
    -webkit-transform: translateZ(-600px) scale(3);
    transform: translateZ(-600px) scale(3);
    z-index: 2;
}

.parallax {
    font-size: 200%;
}

/* centre the content in the parallax layers */
.title {
    text-align: center;
    position: absolute;
    left: 50%;
    top: 50%;
    -webkit-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
}

#group1 {
    z-index: 5; /* slide over group 2 */
}
#group1 .parallax__layer--base {
    background: rgb(102,204,102);
}

#group2 {
    z-index: 3; /* slide under groups 1 and 3 */
}
#group2 .parallax__layer--back {
    background: rgb(123,210,102);
}

</style>
<div class="parallax">
    <div id="group1" class="parallax__group">
      <div class="parallax__layer parallax__layer--base">
        <div class="title">Base Layer</div>
      </div>
    </div>
    <div id="group2" class="parallax__group">
      <div class="parallax__layer parallax__layer--base">
        <div class="title">Base Layer</div>
      </div>
      <div class="parallax__layer parallax__layer--back">
        <div class="title">Background Layer</div>
      </div>
    </div>
</div>

\$\endgroup\$
  • \$\begingroup\$ Obrigado Bruno! I'm gonna try to implement your version tonight and see how it runs. Also the reason I use requestAnimationFrame is because I wanted it to run on every frame to make the animation a little smoother. I'm afraid that with your code it might be a little "jumpy", I will test and let you know. Cheers \$\endgroup\$ – Valeriu Timbuc May 24 '16 at 17:01
  • \$\begingroup\$ Hey man, I implemented your version, but it's not running as well, here's why. With requestAnimationFrame the function was running every frame and it created a smooth effect while scrolling, with your code it only runs on scroll, so if the user scrolls with the mouse wheel, the animation is jumpy and you cant notice that the boxes are moving. I have tried to run rAF on scroll start and keep it running for 1 sec after scroll end, but cancelAnimationFrame doesn't work, I think it's because Im not doing it properly and rAF runs again after cAF. \$\endgroup\$ – Valeriu Timbuc May 25 '16 at 9:17
  • \$\begingroup\$ @ValeriuTimbuc Does something like this work? Unfortantly I think the algorithm I provided is a bit off I will try to fiddle with it a bit later, but something along those lines should work, so try to fiddle with it as well. \$\endgroup\$ – Bruno Costa May 25 '16 at 11:25
  • \$\begingroup\$ Thanks a lot for your help man, really appreciate it, but it's not there yet, it also has a weird behavior from what i saw in the fiddle, sometimes the box goes up and down dsh.re/a720d. \$\endgroup\$ – Valeriu Timbuc May 25 '16 at 12:47
  • \$\begingroup\$ @ValeriuTimbuc Hope that article and this new edit help you better :) \$\endgroup\$ – Bruno Costa May 25 '16 at 17:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.