Parallax animation running well on Chrome but slow on Safari 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 &lt; 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);
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 =&gt; {
  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 =&gt; {
  // Update element positions using transform translate
});

.parallax-element{
  transition: transform 0.2s;
}