Плавный скролл с requestAnimationFrame (не якоря) vanila JS
Всем привет! Получаю в консоль постоянное обновление с ошибкой. (Пример в коде). Возможно реально использовать его без бесконечного вызова requestAnimationFrame или есть еще какое-то более лаконичное и емкое решение с оптимизацией, подскажите? Спасибо!
const
body = document.body,
scrollWrap = document.getElementsByClassName("main")[0],
height = scrollWrap.getBoundingClientRect().height - 1,
speed = 0.1;
var offset = 0;
body.style.height = Math.floor(height) + "px";
function smoothScroll() {
offset += (window.pageYOffset - offset) * speed;
var scroll = "translateY(-" + offset + "px) translateZ(0)";
scrollWrap.style.transform = scroll;
callScroll = requestAnimationFrame(smoothScroll);
}
smoothScroll();
<body>
<div class="main" style="display: flex; flex-direction: column; width: 100vw; height: 200vh">
<span>Text</span>
<div style="width: 10vw; height: 20vh; background-color: #000000"></div>
<span>Text</span>
<span>Text</span>
<div style="width: 10vw; height: 20vh; background-color: #000000"></div>
<span>Text</span>
</div>
<body>
Ответы (1 шт):
Ошибка связана с тем, что переменная callScroll не была объявлена. Чтобы не делать бесконечный вызов requestAnimationFrame, нужно создать обработчик события scroll, и в нём инициировать анимацию, которую также нужно вовремя завершить.
Условие Math.abs(window.pageYOffset - offset) < 0.5 вполне годится для этого.
А вот callScroll нам пригодится, чтобы отменить ранее запланированный кадр при следующем событии scroll.
const scrollWrap = document.getElementsByClassName("main")[0];
const speed = 0.1;
let offset = 0;
let callScroll = null;
function smoothScroll() {
let delta = window.pageYOffset - offset;
offset += delta * speed;
var scroll = "translateY(-" + offset + "px)";
scrollWrap.style.transform = scroll;
if (Math.abs(delta) > 0.5)
callScroll = requestAnimationFrame(smoothScroll);
}
window.addEventListener('scroll', function(e) {
if (callScroll !== null) cancelAnimationFrame(callScroll);
smoothScroll();
});
.main {
display: flex;
flex-direction: column;
width: 100vw;
height: 200vh;
}
.box {
width: 10vw;
height: 20vh;
background-color: #000;
}
<div class="main">
<span>Text</span>
<div class="box"></div>
<span>Text</span>
<span>Text</span>
<div class="box"></div>
<span>Text</span>
<span>Text</span>
</div>
Есть более лаконичное решение без requestAnimationFrame, при помощи transition:
const scrollWrap = document.getElementsByClassName('main')[0];
window.addEventListener('scroll', function(e) {
let scroll = 'translateY(-' + window.pageYOffset + 'px)';
scrollWrap.style.transform = scroll;
});
.main {
display: flex;
flex-direction: column;
width: 100vw;
height: 200vh;
transition: 0.5s cubic-bezier(0, 0, 0.25, 1.0);
}
.box {
width: 10vw;
height: 20vh;
background-color: #000;
}
<div class="main">
<span>Text</span>
<div class="box"></div>
<span>Text</span>
<span>Text</span>
<div class="box"></div>
<span>Text</span>
<span>Text</span>
</div>
