1
\$\begingroup\$

This is the continuation of a previous question I posted here.

I've improved (at least I think so) the initial script from the previous question, now storing the data in an array, it runs slightly better, but still sometimes there's some lag in safari. I've tried to call requestAnimationFrame on scroll and cancelAnimationFrame on scroll end, but the animation was a little jumpy so I removed that.

This is the updated code:

var animationId,
    parallaxElements = [];

// Push elements to array
s.parallaxElements.each(function (i) {
  var i = {
    element: $(this),
    ratio: $(this).data('parallax-ratio'),
    offset: $(this).closest('.parallax-section').offset().top - 60,
    current: 0
  };

  parallaxElements.push(i);
});

// Animation
function animation() {
  var scrollPos = s.theWindow.scrollTop();

  for (var i = 0; i < parallaxElements.length; i++) {
    var self = parallaxElements[i],
        current = self.current,
        position = current - (current - (self.offset - scrollPos) * self.ratio) * 0.1;

    if (position !== current) {
      self.element.css('transform', 'translateY(' + position + 'px)');
      self.current = position;
    }
  }

  animationId = window.requestAnimationFrame(animation);
}

window.requestAnimationFrame(animation);
\$\endgroup\$
2
\$\begingroup\$
    parallaxElements = [];

// Push elements to array
s.parallaxElements.each(function (i) {
  var i = {
    element: $(this),
    ratio: $(this).data('parallax-ratio'),
    offset: $(this).closest('.parallax-section').offset().top - 60,
    current: 0
  };

  parallaxElements.push(i);
});

This looks like transforming one array to another on a 1:1 basis. This is best done with array.map. Also, you should cache the result of $(this) so that you don't call it repeatedly. Additionally, this inside a jquery.each is the current item, which should be the same as the first argument in array.map's callback.

var parallaxElements = s.parallaxElements.get().map(element => {
  const wrappedElement = $(element);

  return {
    element: wrappedElement,
    ratio: wrappedElement.data('parallax-ratio'),
    offset: wrappedElement.closest('.parallax-section').offset().top - 60,
    current: 0
  };
});

Now for your animation, I suggest you avoid using requestAnimationFrame in this case. It will still be running even when no position changes are taking effect, eating up cycles.

Since your parallax effect only takes place only when scroll position has changed, I suggest you listen for the window's scroll event and do changes when scrolled. As for the movement animation, use CSS transitions.

$(window).scroll(event => {
  // Update element positions using transform translate
});

.parallax-element{
  transition: transform 0.2s;
}
\$\endgroup\$
  • \$\begingroup\$ May I ask what's the difference between event => {} and function (event) {}, never seen your way before. \$\endgroup\$ – Valeriu Timbuc May 26 '16 at 15:34
  • \$\begingroup\$ Also const wrappedElement = $(element); returns an empty selector. \$\endgroup\$ – Valeriu Timbuc May 26 '16 at 15:40
  • \$\begingroup\$ @ValeriuTimbuc That's arrow functions, in ES6. The empty element, will update answer. \$\endgroup\$ – Joseph May 26 '16 at 17:22
  • \$\begingroup\$ Thanks, I've implement your way to create the array. I've also tried again to do it on scroll and using css transitions, but it's not the same effect, since the duration is the same regardless of how much I scrolled, I've also tried to calculate how much distance was scrolled and set the duration dynamically, but still not the same. Now I'm still using rAF, I'm trying to set a timeout on scroll so I can stop the animation after 3 seconds, but scroll is not accurate, and sometimes it stops abruptly. Here's the code, is there a way to have a accurate scroll event? \$\endgroup\$ – Valeriu Timbuc May 27 '16 at 8:48

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.