Search code examples
javascriptanimationscrollrequestanimationframe

Migrate jQuery animate({scrollLeft: x}) to vanilla JS using window.requestAnimationFrame()


I'm trying to migrate some jQuery code that performs a smooth scroll (back and forth) in an horizontal image gallery when clicking over its images (putting the clicked image in the center of the browser viewport):

jQuery:

$('.page-template-format-horizontal .exhibit-image').on('click', function (e) {
    var slideWidth = $(this).width();
    var windowWidth = $(window).width();
    var scrollTo = $(this).position().left;
    var offset = scrollTo - (windowWidth / 2) + (slideWidth / 2);
    $('html, body').animate({
        scrollLeft: offset
    }, 500);
    e.preventDefault();
});

This is the vanilla JS code I've come up with. I know that I can use the behaviour: 'smooth' option in the window.scrollBy() function, but I'd like to use the window.requestAnimationFrame() function instead because I need to support some older Safari versions that don't support the smooth behavior option.

JS:

document.querySelector('.page-template-format-horizontal .exhibit-image').addEventListener('click', function (e) {
    var slideWidth = e.currentTarget.getBoundingClientRect().width;
    var windowWidth = window.innerWidth;
    var scrollTo = e.currentTarget.offsetLeft;
    var offset = scrollTo - (windowWidth / 2) + (slideWidth / 2);

    var duration = 50;
    var startLocation = window.pageXOffset;
    var endLocation = offset;
    var distance = endLocation - startLocation;
    var increments = distance / (duration / 16);

    function step() {
        window.scrollBy(increments, 0);
        if ( window.pageXOffset <= endLocation) {
            window.requestAnimationFrame(step);
        }
    }
    window.requestAnimationFrame(step);
    e.preventDefault();
});

It quite works, but just when scrolling to the right, not to the left. Any help would be appreciated.


Solution

  • I managed to solve this myself. This is the code I came up with:

    document.querySelectorAll('.page-template-format-horizontal .exhibit-image').forEach(function(item) {
        item.addEventListener('click', function (e) {
            var slideWidth = e.currentTarget.getBoundingClientRect().width;
            var windowWidth = window.innerWidth;
            var scrollTo = e.currentTarget.offsetLeft;
            var startLocation = window.pageXOffset;
            var endLocation = scrollTo - (windowWidth / 2) + (slideWidth / 2);
            var distance = endLocation - startLocation;
            var duration = 400;
            var easing = function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
            var start;
    
            if (!distance) {
                return;
            }
    
            function step(timestamp) {
                start = start || timestamp;
                var time = timestamp - start;
                var percent = Math.min(time / duration, 1);
                percent = easing(percent);
                window.scrollTo(parseInt(startLocation + distance * percent), window.scrollY);
                if (time < duration) {
                    window.requestAnimationFrame(step);
                }
            }
            window.requestAnimationFrame(step);
            e.preventDefault();
        });
    });