Плавный скролл используя математическую функцию
Есть вот такой код
let currentScreen = 0
addEventListener('wheel', function(e) {
if (e.deltaY < 0) currentScreen--
else currentScreen++
if (currentScreen < 0) currentScreen = 4
if (currentScreen > 4) currentScreen = 0
})
function ease(t) {
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
}
function ПлавныйСкролл() {
window.scroll(0, currentScreen * innerHeight)
requestAnimationFrame(ПлавныйСкролл)
}
requestAnimationFrame(ПлавныйСкролл)
body {
margin: 0;
overflow: hidden;
}
.screen {
height: 100vh;
}
.screen_1 {
background-color: lightblue;
}
.screen_2 {
background-color: lightgreen;
}
.screen_3 {
background-color: tomato;
}
.screen_4 {
background-color: moccasin;
}
.screen_5 {
background-color: chocolate;
}
<div class="screen screen_1">1</div>
<div class="screen screen_2">2</div>
<div class="screen screen_3">3</div>
<div class="screen screen_4">4</div>
<div class="screen screen_5">5</div>
и вот такая функция, которую нашел на просторах интернета
easeInOutCubic: t => t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1.
Не могу понять, что такое t и как это всё соединить вместе.
Ответы (3 шт):
Автор решения: hu-fo
→ Ссылка
JS
var startScrollValue = document.documentElement.scrollTop
// чтобы после перезагруки страницы было правильное значение
var currentScreen = Math.floor(startScrollValue / innerHeight)
// значение до которого интерполируется значение переменной i
var finalValue = innerHeight
var duration = 1500
var startTime = null
var endTime = null
var i = 0
var isoktoscroll = true
// здесь будет храниться id которое возвращает функция requestAnimationFrame
// чтобы в последующем отменить её
var rid = null
addEventListener('wheel', function(e) {
if (!isoktoscroll) return
isoktoscroll = false
if (e.deltaY < 0) currentScreen--
else currentScreen++
if (currentScreen < 0) currentScreen = 4
if (currentScreen > 4) currentScreen = 0
requestAnimationFrame(startScrolling)
})
// эта функция должна принимать аргумент в диапазоне 0-1
function ease(t) {
// какое-то безумие
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
}
function startScrolling(timestamp) {
startTime = timestamp
endTime = startTime + duration
ПлавныйСкролл(startTime)
}
function ПлавныйСкролл(now) {
rid = requestAnimationFrame(ПлавныйСкролл)
var p = (now - startTime) / duration
i = startScrollValue + (finalValue * currentScreen - startScrollValue) * ease(p)
window.scroll(0, i)
if (now > endTime) {
// устанавливается конечное значение i
// чтобы при последующем скролле начать с него
startScrollValue = i
// можно снова скроллить
isoktoscroll = true
cancelAnimationFrame(rid)
return
}
}
// чтобы при изменении высота всё правильно отображалось
onresize = () => {
finalValue = innerHeight
startScrollValue = currentScreen * finalValue
window.scroll(0, startScrollValue)
}
body {
margin: 0;
overflow: hidden;
}
.screen {
height: 100vh;
}
.screen_1 {
background-color: lightblue;
}
.screen_2 {
background-color: lightgreen;
}
.screen_3 {
background-color: tomato;
}
.screen_4 {
background-color: moccasin;
}
.screen_5 {
background-color: chocolate;
}
<div class="screen screen_1">1</div>
<div class="screen screen_2">2</div>
<div class="screen screen_3">3</div>
<div class="screen screen_4">4</div>
<div class="screen screen_5">5</div>
Css(по совету этого товарища)
var currentScreen = 0
var isoktoscroll = true
addEventListener('wheel', function(e) {
if(!isoktoscroll) return
isoktoscroll = false
if (e.deltaY < 0) currentScreen--
else currentScreen++
if (currentScreen < 0) currentScreen = 4
if (currentScreen > 4) currentScreen = 0
container.style.transform = `translateY(-${currentScreen * 100}vh)`
setTimeout(() => isoktoscroll = true, 1500)
})
body {
margin: 0;
overflow: hidden;
}
.screen {
height: 100vh;
}
.screen_1 {
background-color: lightblue;
}
.screen_2 {
background-color: lightgreen;
}
.screen_3 {
background-color: tomato;
}
.screen_4 {
background-color: moccasin;
}
.screen_5 {
background-color: chocolate;
}
.container {
transition: 1.5s ease-in-out;
}
<div id="container" class="container">
<div class="screen screen_1">1</div>
<div class="screen screen_2">2</div>
<div class="screen screen_3">3</div>
<div class="screen screen_4">4</div>
<div class="screen screen_5">5</div>
</div>
Автор решения: Qwertiy
→ Ссылка
~function () {
var waiting = false
window.addEventListener('wheel', function (e) {
if (waiting) return
waiting = true
setTimeout(() => { waiting = false }, 500)
var cur = document.querySelector('.active')
var parent = cur.parentElement
var oldAbove = cur.previousElementSibling || parent.lastElementChild
var oldBellow = cur.nextElementSibling || parent.firstElementChild
var next = e.deltaY < 0 ? oldAbove : oldBellow
var newAbove = next.previousElementSibling || next.parentElement.lastElementChild
var newBellow = next.nextElementSibling || parent.firstElementChild
oldAbove.classList.remove('above')
cur.classList.remove('active')
oldBellow.classList.remove('bellow')
newAbove.classList.add('above')
next.classList.add('active')
newBellow.classList.add('bellow')
})
}()
html, body, main {
margin: 0;
overflow: hidden;
height: 100%;
}
.screen {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
transform: translateY(100%);
transition: transform ease-in-out .5s;
z-index: 1;
}
.screen:last-child {
z-index: 0;
}
.screen.active, .screen.bellow {
z-index: 2;
}
.screen.above:last-child {
z-index: 1;
}
.screen.above {
transform: translateY(-100%);
}
.screen.active {
transform: translateY(0);
}
<main>
<div class="screen active" style="background-color: lightblue;" >1</div>
<div class="screen bellow" style="background-color: lightgreen;">2</div>
<div class="screen" style="background-color: tomato;" >3</div>
<div class="screen" style="background-color: moccasin;" >4</div>
<div class="screen above" style="background-color: chocolate;" >5</div>
</main>
Автор решения: Stranger in the Q
→ Ссылка
Входной аргумент для функции сглаживания обычно называют t, время.
Допустим у Вас уже есть анимация, с линейно меняющимся параметром, при этом время изменяется в интервале от 0 до 1, где 0 - начало анимации, а 1 - конец анимации:
requestAnimationFrame(draw);
function draw(t) {
t /= 3000; // разделим вермя в миллисикундах, на продолжительность
// анимации в миллисекундах, тоесть тут 3 секунды,
// иными словани через 3 секунды значение переменной t станет больше 1
t %= 1; // возьмем остаток от деления на 1, таким образом по прошествии
// трех секунд, время опять начнется с нуля
// двигаем круг
c.setAttribute('transform', `translate(${250*t}, 0)`);
requestAnimationFrame(draw);
}
<svg style="border: solid"><circle id="c" r="20" cx="20" cy="75" /></svg>
Вот что необходимо сделать чтобы применить функцию сглаживания к времени:
t = easeInOutCubic(t);
let easeInOutCubic = t => t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1
requestAnimationFrame(draw);
function draw(t) {
t /= 3000;
t %= 1;
t = easeInOutCubic(t); // применяем функцию сглаживания к времени
c.setAttribute('transform', `translate(${250*t}, 0)`);
requestAnimationFrame(draw);
}
<svg style="border: solid"><circle id="c" r="20" cx="20" cy="75" /></svg>
Или вот пример другой функции сглаживания
let easeElastic = t => Math.pow(2,-10*t) * Math.sin((t-0.3/4)*(2*Math.PI)/0.3) + 1
requestAnimationFrame(draw);
function draw(t) {
t /= 3000;
t %= 1;
t = easeElastic(t);
c.setAttribute('transform', `translate(${250*t}, 0)`);
requestAnimationFrame(draw);
}
<svg style="border: solid" width="400" ><circle id="c" r="20" cx="20" cy="75"/></svg>