My goal is to implement a smooth scrolling experience with a bounce effect when the user over-scrolls, meaning they scroll too much to the top or bottom. I found an answer on Stack Overflow that explains how to achieve smooth scrolling, but I also want to add a bounce effect when the scroll goes out of bounds. Something similar to this:
Note: The GIF shown here is for mobile, but I want to implement it for all browsers, including desktop and mobile.
https://i.sstatic.net/4zuSZ.gif
I attempted to incorporate the following code into the interval:
// 50 = Padding
if (tgt.scrollTop < 50 || tgt.scrollTop > tgt.scrollHeight - tgt.offsetHeight - 50) {
pos = Math.bounce(step++, start, change, steps);
} else {
pos = Math.easeOut(step++, start, change, steps);
}
However, the desired effect is not achieved. The bounce doesn't occur at the correct scrolling time, and it doesn't look realistic enough; it's not large enough.
(I'm aiming for something similar to this effect, but I want to develop it myself and understand the code.)
How can I create a bouncing effect when the scroll reaches the top or bottom?
I'm not looking for answers that lead me to a complex library because I want to create this effect using vanilla JavaScript.
console.clear();
/* Smooth scroll */
Math.easeOut = function(t, b, c, d) {
t /= d;
return -c * t * (t - 2) + b;
};
// Out Back Quartic
Math.bounce = function(t, b, c, d) {
var ts = (t /= d) * t;
var tc = ts * t;
return b + c * (4 * tc + -9 * ts + 6 * t);
};
(function() { // do not mess global space
var
interval, // scroll is being eased
mult = 0, // how fast do we scroll
dir = 0, // 1 = scroll down, -1 = scroll up
steps = 50, // how many steps in animation
length = 30; // how long to animate
function MouseWheelHandler(e) {
e.preventDefault(); // prevent default browser scroll
clearInterval(interval); // cancel previous animation
++mult; // we are going to scroll faster
var delta = -Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
if (dir != delta) { // scroll direction changed
mult = 1; // start slowly
dir = delta;
}
for (var tgt = e.target; tgt != document.documentElement; tgt = tgt.parentNode) {
var oldScroll = tgt.scrollTop;
tgt.scrollTop += delta;
if (oldScroll != tgt.scrollTop) break;
}
var start = tgt.scrollTop;
var end = start + length * mult * delta; // where to end the scroll
var change = end - start; // base change in one step
var step = 0; // current step
interval = setInterval(function() {
var pos;
// 50 = Padding
if (tgt.scrollTop < 50 || tgt.scrollTop > tgt.scrollHeight - tgt.offsetHeight - 50) {
pos = Math.bounce(step++, start, change, steps);
} else {
pos = Math.easeOut(step++, start, change, steps);
}
tgt.scrollTop = pos;
if (step >= steps) { // scroll finished without speed up - stop by easing out
mult = 0;
clearInterval(interval);
}
}, 10);
}
var myP = document.getElementById('myP');
myP.addEventListener("mousewheel", MouseWheelHandler, false);
//window.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
})();
p {
height: 300px;
overflow: auto;
background-color: orange;
padding: 50px 0;
}
<p id="myP">Lorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla...
(Content continues with lorem ipsum text)