JSFiddle - React, Tailwind, and code Playground
by Christopher
HTML
<div class="boxes-container">
<div class="box">
<h2>0</h2>
</div>
<div class="box">
<h2>1</h2>
</div>
<div class="box">
<h2>2</h2>
</div>
<div class="box">
<h2>3</h2>
</div>
<div class="box">
<h2>4</h2>
</div>
<div class="box">
<h2>5</h2>
</div>
<div class="box">
<h2>6</h2>
</div>
</div>
<div class="boxes-container v30" data-direction="invert">
<div class="box">
<h2>A</h2>
</div>
<div class="box">
<h2>B</h2>
</div>
<div class="box">
<h2>C</h2>
</div>
<div class="box">
<h2>D</h2>
</div>
<div class="box">
<h2>E</h2>
</div>
<div class="box">
<h2>F</h2>
</div>
<div class="box">
<h2>G</h2>
</div>
</div>
<div class="boxes-container v60">
<div class="box">
<h2>0</h2>
</div>
<div class="box">
<h2>1</h2>
</div>
<div class="box">
<h2>2</h2>
</div>
<div class="box">
<h2>3</h2>
</div>
<div class="box">
<h2>4</h2>
</div>
<div class="box">
<h2>5</h2>
</div>
<div class="box">
<h2>6</h2>
</div>
</div>
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Sans-Serif;
}
.boxes-container {
position: fixed;
display: flex;
flex-wrap: nowrap;
flex-direction: row;
white-space: nowrap;
min-width: -min-content;
}
.v30 {
top: 30vh;
}
.v60 {
top: 60vh;
}
.box {
position: relative;
margin: 0 !important;
padding: 0 50px;
}
.box h2 {
font-size: 5rem;
}
JavaScript
const eventHandler = (e) => {
document.querySelectorAll(".boxes-container").forEach(container => {
const cur = +container.dataset.cur || 0;
container.dataset.before = container.dataset.cur;
container.dataset.scrollspeed = (+container.dataset.scrollspeed || 0) +1;
setTimeout(() => {
container.dataset.scrollspeed = +container.dataset.scrollspeed -1;
}, 33 * +container.dataset.scrollspeed);
let moveByPixels = Math.round(e.deltaY / (10 - Math.min(+container.dataset.scrollspeed,9)));
if (container.dataset.direction == "invert") {
moveByPixels *= -1;
}
container.style.left = `${cur + -moveByPixels}px`;
container.dataset.cur = cur + -moveByPixels;
});
};
window.addEventListener("wheel", eventHandler);
window.addEventListener("mousewheel", eventHandler);
const observer = new IntersectionObserver((entries, opts) => {
entries.forEach(entry => {
entry.target.classList.toggle('visible', entry.isIntersecting);
});
document.querySelectorAll(".boxes-container").forEach(container => {
const before = (+container.dataset.before || 0),
current = (+container.dataset.cur || 0),
diff = before - current,
boxes = [...container.querySelectorAll(".box")],
visible = [...container.querySelectorAll(".box.visible")],
first = boxes.indexOf(visible[0]),
last = boxes.indexOf(visible[visible.length - 1]),
adjust = (by) => {
container.dataset.cur = +container.dataset.cur + by;
container.dataset.before = +container.dataset.before + by;
container.style.left = +container.dataset.cur + 'px';
};
if (diff >= 0) {
if (first > 0) { // move the first to the end
const box = boxes[0];
box.parentNode.append(box);
adjust(box.clientWidth);
}
} else {
if (last == 0 || first == 0) { // move the to first
const box = boxes[boxes.length - 1];
box.parentNode.prepend(box);
adjust(-box.clientWidth);
...