问题
I have been wanting to implement smooth scrolling in vanilla JS for some time, but could not find any adequate implementations or methods. However, I recently came across the concept of linear interpolation (LERP), which seems adequate unlike every other method.
Although content about LERP smooth scrolling seems scarce on the web, I just found two great articles (one, two) about LERP in general and an example of LERP being used for smooth scrolling on codepen.
The example, notably, seems unnecessarily complex due to a seemingly unnecessary amount of nested containers, so I created a minimal version of it shown below. The minimal version seems to be working, but I still have a few concerns and questions.
const
b = document.body,
m = document.querySelector('main'),
p = document.querySelector('p')
let
state = {
scroll: {
height: 0,
offset: 0,
speed: 0.075,
}
},
direction
document.addEventListener('DOMContentLoaded', () => {
state.scroll.height = m.getBoundingClientRect().height
b.style.height = `${Math.floor(state.scroll.height)}px`
})
b.addEventListener('wheel', (e) => {
direction = e.deltaY > 0 ? 'scrollDown' : 'scrollUp'
// renderLoop()
})
function renderLoop() {
// const
// difference = 'scrollDown' ? pageYOffset - state.scroll.offset : state.scroll.offset - pageYOffset
state.scroll.offset += Math.floor((pageYOffset - state.scroll.offset) * state.scroll.speed)
m.style.transform = `translateY(-${ state.scroll.offset }px)`
// if (difference > 20) requestAnimationFrame(renderLoop)
requestAnimationFrame(renderLoop)
}
renderLoop()
html, body {
width: 100%;
height: 100%;
margin: 0;
font-size: 48px;
}
main {
width: 100%;
position: fixed;
left: 0;
top: 0;
border: solid green 3px; box-sizing: border-box;
}
p {
width: 100%;
max-width: 500px;
position: relative;
left: 50%;
transform: translateX(-50%);
margin: 0;
border: solid red 3px; box-sizing: border-box;
}
<main>
<p>One<br>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Error soluta, quidem voluptatibus quo iusto iure adipisci, ullam quis unde dolore, nisi impedit in. Veniam, suscipit? Atque aut et incidunt aliquid. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Exercitationem labore dolore aut atque harum, quo necessitatibus molestias laborum fuga beatae explicabo laudantium asperiores doloremque optio iure, assumenda voluptate unde voluptates? Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet inventore, voluptates corrupti illo impedit in. Recusandae, praesentium temporibus tempore totam cupiditate ratione maxime numquam corporis repudiandae. Veniam ullam rerum quo?</p>
<p>Two<br>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Error soluta, quidem voluptatibus quo iusto iure adipisci, ullam quis unde dolore, nisi impedit in. Veniam, suscipit? Atque aut et incidunt aliquid. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Exercitationem labore dolore aut atque harum, quo necessitatibus molestias laborum fuga beatae explicabo laudantium asperiores doloremque optio iure, assumenda voluptate unde voluptates? Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet inventore, voluptates corrupti illo impedit in. Recusandae, praesentium temporibus tempore totam cupiditate ratione maxime numquam corporis repudiandae. Veniam ullam rerum quo?</p>
<p>Three<br>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Error soluta, quidem voluptatibus quo iusto iure adipisci, ullam quis unde dolore, nisi impedit in. Veniam, suscipit? Atque aut et incidunt aliquid. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Exercitationem labore dolore aut atque harum, quo necessitatibus molestias laborum fuga beatae explicabo laudantium asperiores doloremque optio iure, assumenda voluptate unde voluptates? Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet inventore, voluptates corrupti illo impedit in. Recusandae, praesentium temporibus tempore totam cupiditate ratione maxime numquam corporis repudiandae. Veniam ullam rerum quo?</p>
</main>
One concern is that for some reason it breaks unless I call renderLoop
from the global scope and let requestAnimationFrame
run indefinitely without a killswitch, which, instinctively, does not seem like a good idea?
I am also confused by the fact that, although I am offsetting the content container via translate
, there still appears to be a scrollbar that scrolls. And yet, notably, this does not appear to interfere with the effect at all. However, should LERP smooth scrolling via translate
not bypass scrollbars and scrolling altogether?
Additionally, it seems as though the only purpose of setting the height
of body
to that of main
is to make use of pageYOffset
. I suspect there are better ways to approach the problem that do not involve using pageYOffset
. Further, you would think that the combination of offsetting with translate
and the page being scrolled would increase the rate at which the content is scrolled?
Anyways, I have just started coding again after an almost year-long absence, and I am not sure if I ever familiarized myself with requestAnimationFrame
, so any information, guidance, or solutions would be greatly appreciated.
[ edit + update ]
Here is a codepen of a "working" (?) version, which can be seen below, that I created in attempt to address and solve some concerns or issues present in the version above. However, it clearly does not have the same effect due to, I believe, the offset value being a percentage instead of pixels, which was chosen in an attempt to standardize the "speed" at which it scrolls across "platforms" (i.e. devices, browsers, etc).
const
b = document.body,
m = document.querySelector('main')
let
scrollState = {
overflow: 0,
target: 0,
position: 0,
rate: 10,
speed: 0.2
}
b.addEventListener('wheel', (e) => {
scrollState.target -= scrollState.rate * (e.deltaY / 100)
if (scrollState.target > 0) scrollState.target = 0
else if (scrollState.target < -100) scrollState.target = -100
renderLoop()
})
function renderLoop() {
const
rawDiff = Math.abs(scrollState.position - scrollState.target),
direction = scrollState.position - scrollState.target < 0 ? -1 : 1,
diff = rawDiff * scrollState.speed * direction,
newPosition = scrollState.position - diff
m.style.transform = `translateY(${newPosition}%)`
scrollState.position = newPosition
if (Math.floor(rawDiff)) requestAnimationFrame(renderLoop)
}
html, body {
width: 100%;
height: 100%;
margin: 0;
font-size: 48px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
border: solid blue 4px; box-sizing: border-box;
}
main {
width: 100%;
position: fixed;
left: 0;
top: 0;
border: solid green 2px; box-sizing: border-box;
}
p {
width: 100%;
max-width: 500px;
position: relative;
left: 50%;
transform: translateX(-50%);
margin: 0;
border: solid red 2px; box-sizing: border-box;
}
<main>
<p>One<br>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Error soluta, quidem voluptatibus quo iusto iure adipisci, ullam quis unde dolore, nisi impedit in. Veniam, suscipit? Atque aut et incidunt aliquid. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Exercitationem labore dolore aut atque harum, quo necessitatibus molestias laborum fuga beatae explicabo laudantium asperiores doloremque optio iure, assumenda voluptate unde voluptates? Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet inventore, voluptates corrupti illo impedit in. Recusandae, praesentium temporibus tempore totam cupiditate ratione maxime numquam corporis repudiandae. Veniam ullam rerum quo?</p>
<p>Two<br>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Error soluta, quidem voluptatibus quo iusto iure adipisci, ullam quis unde dolore, nisi impedit in. Veniam, suscipit? Atque aut et incidunt aliquid. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Exercitationem labore dolore aut atque harum, quo necessitatibus molestias laborum fuga beatae explicabo laudantium asperiores doloremque optio iure, assumenda voluptate unde voluptates? Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet inventore, voluptates corrupti illo impedit in. Recusandae, praesentium temporibus tempore totam cupiditate ratione maxime numquam corporis repudiandae. Veniam ullam rerum quo?</p>
<p>Three<br>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Error soluta, quidem voluptatibus quo iusto iure adipisci, ullam quis unde dolore, nisi impedit in. Veniam, suscipit? Atque aut et incidunt aliquid. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Exercitationem labore dolore aut atque harum, quo necessitatibus molestias laborum fuga beatae explicabo laudantium asperiores doloremque optio iure, assumenda voluptate unde voluptates? Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet inventore, voluptates corrupti illo impedit in. Recusandae, praesentium temporibus tempore totam cupiditate ratione maxime numquam corporis repudiandae. Veniam ullam rerum quo?</p>
</main>
来源:https://stackoverflow.com/questions/64872594/lerp-smooth-scrolling