As the title says, it works perfectly fine on Chrome. But in Safari, it just sets the page to the desired top and and left position. Is this an expected behaviour? Is there
The solution with the smoothest performance, especially if you want to incorporate easing is to use requestAnimationFrame:
const requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
const step = (timestamp) => {
window.scrollBy(
0,
1, // or whatever INTEGER you want (this controls the speed)
);
requestAnimationFrame(step);
};
requestAnimationFrame(step);
if you want to later cancel the scroll, you need to have a reference to your requestAnimationFrame (do this everywhere you use requestAnimationFrame(step)):
this.myRequestAnimationFrame = requestAnimationFrame(step);
const cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;
cancelAnimationFrame(this.myRequestAnimationFrame);
create an array of 60 elements (requestAnimationFrame usually calls 60 times per second. It's technically whatever the refresh rate of the browser is, but 60 is the most common number.) We are going to fill this array non-linearly then use those numbers to control how much to scroll at each step of requestAnimationFrame:
let easingPoints = new Array(60).fill(0)
choose an easing function. Let's say we're doing a cubic ease-out:
function easeCubicOut(t) {
return --t * t * t + 1;
}
create a dummy array and fill it with data piped through the easing function. You'll see why we need this in a moment:
// easing function will take care of decrementing t at each call (too lazy to test it at the moment. If it doesn't, just pass it a decrementing value at each call)
let t = 60;
const dummyPoints = new Array(60).fill(0).map(()=> easeCubicOut(t));
const dummyPointsSum = dummyPoints.reduce((a, el) => {
a += el;
return a;
}, 0);
map easingPoints using the help of each dummyPoint ratio to dummyPointsSum:
easingPoints = easingPoints.map((el, i) => {
return Math.round(MY_SCROLL_DISTANCE * dummyPoints[i] / dummyPointsSum);
});
in your scroll function, we'll make a few adjustments:
const requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
let i = 0;
const step = (timestamp) => {
window.scrollBy(
0,
easingPoints[i],
);
if (++i === 60) {
i = 0;
return setTimeout(() => {
this.myRequestAnimationFrame = requestAnimationFrame(step);
}, YOUR_TIMEOUT_HERE);
}
};
this.myRequestAnimationFrame = requestAnimationFrame(step);