Skip to content

Instantly share code, notes, and snippets.

@denisso
Last active March 19, 2023 12:48
Show Gist options
  • Save denisso/3f4d1dcb70c545e349405794da7bb946 to your computer and use it in GitHub Desktop.
Save denisso/3f4d1dcb70c545e349405794da7bb946 to your computer and use it in GitHub Desktop.
smooth scroll
<!-- https://codepen.io/den4ik_rus/pen/yLxKoea -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title></title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container {
height: calc(100vh - 100px);
overflow: hidden;
display: flex;
}
.scrollWrapper {
flex: 1;
background-color: gray;
}
.vScroolBox {
flex: 0;
overflow: auto;
flex-basis: 20px;
}
.hiddenScrollBar {
width: 1px;
}
.box {
position: relative;
font-size: 4rem;
font-weight: 900;
}
.item {
height: 400px;
border: solid;
text-align: center;
}
header {
display: flex;
height: 100px;
justify-content: center;
align-items: center;
gap: 1rem;
font-size: 1.2rem;
}
</style>
</head>
<body>
<header>
<span>goto section </span>
<select class="selectSection">
<option disabled>Choose section</option>
</select>
</header>
<div class="container">
<div class="scrollWrapper">
<div class="box">
</div>
</div>
<div class="vScroolBox">
<div class="hiddenScrollBar"></div>
</div>
</div>
<script>
const vScroolBox = document.querySelector(".vScroolBox");
const hiddenScrollBar = document.querySelector(".hiddenScrollBar");
const container = document.querySelector(".container");
const box = document.querySelector(".box");
const selectSection = document.querySelector(".selectSection");
const state = { isScroll: false };
for (let i = 0; i < 20; i++) {
box.insertAdjacentHTML("beforeend", `<div class="item">${i}</div>`);
selectSection.insertAdjacentHTML("beforeend", `<option>${i}</option >`);
}
selectSection.addEventListener("change", () => {
const value = selectSection.value;
scrollTo(box.children[value].offsetTop);
});
function throttle(cb, delay = 1000) {
let shouldWait = false;
let waitingArgs;
const timeoutFunc = () => {
if (waitingArgs === null) {
shouldWait = false;
} else {
cb(...waitingArgs);
waitingArgs = null;
setTimeout(timeoutFunc, delay);
}
};
return (...args) => {
if (shouldWait) {
waitingArgs = args;
return;
}
cb(...args);
shouldWait = true;
setTimeout(timeoutFunc, delay);
};
}
const duration = 300;
const step = () => {
if (!state.isScroll) return;
state.progress = (performance.now() - state.startTime) / duration;
if (state.progress > 0.9999) {
state.progress = 1;
state.isScroll = false;
}
// box.style.top = -state.currY + "px";
// https://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
state.currY = state.prevY + state.progress * state.diffY;
box.style.transform = `translateY(${-state.currY}px)`;
window.requestAnimationFrame(step);
};
const scroll = throttle((e) => {
state.startTime = performance.now();
state.prevY = state.currY;
state.nextY = vScroolBox.scrollTop;
state.diffY = state.nextY - state.prevY;
if (!state.isScroll) {
state.isScroll = true;
window.requestAnimationFrame(step);
}
}, 100);
state.currY = vScroolBox.scrollTop;
state.prevY = vScroolBox.scrollTop;
vScroolBox.addEventListener("scroll", scroll);
container.addEventListener("wheel", (e) => {
scroll();
const scrollAmount = document.documentElement.clientHeight / 5;
if (e.wheelDeltaY > 0) {
vScroolBox.scrollTop -= scrollAmount;
} else {
vScroolBox.scrollTop += scrollAmount;
}
});
function scrollTo(amount) {
scroll();
vScroolBox.scrollTop = amount;
}
function scrollMore(amount) {
scroll();
vScroolBox.scrollTop += amount;
}
hiddenScrollBar.style.height = box.getBoundingClientRect().height + "px";
</script>
</body>
</html>
@denisso
Copy link
Author

denisso commented Mar 14, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment