Issue
I'm essentially trying to bind vertical scrolling of the page to scroll horizontally instead. Also using scroll-snapping and smooth scroll behavior to scroll to the next/previous "page".
Please watch the video to see the strange behavior of the script and how it interacts with the stylesheet, you can recreate the bug with the codepen. I'm not sure whats causing the glitch. I added a timeout in the code of 400ms, which was intended to stop users from accidentally triggering the scroll in both directions at once.
The intent is to allow users to scroll to the next element only once the current element is in view.
When scroll-snap is disabled, the scrollBy function does not work, only moving slightly (instead of the full window.innerWidth)
var waiting
if (waiting == null) {
waiting = 0
}
document.querySelector(".main-nav").addEventListener("wheel", (event) => {
event.preventDefault();
if (waiting === 0) {
waiting = 1
console.log("started wait");
if (event.deltaY > 0) {
scrlFF();
setTimeout(finishedWaiting, 400);
} else if (event.deltaY < 0) {
scrlFR();
setTimeout(finishedWaiting, 400);
}
}
});
function finishedWaiting() { console.log("finished wait"); waiting = 0 }
function scrlFF() { document.querySelector(".main-nav").scrollBy({ left: window.innerWidth, behavior: 'smooth' }); }
function scrlFR() { document.querySelector(".main-nav").scrollBy({ left: -window.innerWidth, behavior: 'smooth' }); }
.a { background: red;
min-width: 100vw; height: 100vh; display: flex; scroll-snap-align: center; scroll-snap-stop: always;}
.a ~ .a { background: blue;}
.a ~ .a ~ .a { background: red;}
.a ~ .a ~ .a ~ .a { background: blue;}
* { overflow-y: hidden }
.main-nav {display: flex;flex-direction: row;height: 100%;scroll-snap-type: x mandatory;overflow-x: scroll;scroll-behavior: smooth;scrollbar-width: none;width: inherit;}
<div class="main-nav"><div class="a"></div><div class="a"></div><div class="a"></div><div class="a"></div></div>
Solution
Mouse wheel and page scrolling are often a challenge.
This is really just a slight twist on what you have but with some additions to smooth out the scroll. Not perfect but really only odd on fast up/down wheel scroll.
Things in play/changes:
- Use a cleaner CSS for even/odd color; now you can have any number of elements without altering the CSS.
- Setup a data attribute and target it with CSS for our pointer event; so we just set true/false for that. So setting the attribute value is a smaller DOM hit perhaps and certainly easier to code.
- Small event on the window "wheel" to smooth the scroll a bit. This is part of the "clunky" scroll seen so we just prevent it.
- Some small bit of logic to detect the start/end of the scroll so we can toggle it ON when we are at the start/end.
- Dispense with the
setTimeout
as we can do this with some events - thescrollend
is the key here where we toggle when it is fired. - DRY up the code with one function
function scrollContainer(main, plus) {
instead of two; pass a Boolean for the left/right. - Cleaner with NO globals since we moved the toggle to the data attribute; we can just pass a reference to the "main" to the scroll function.
- Many/most of the
let
can probably beconst
but left that as a separate exercise.
// smooths out the scroll on fast wheel up/down
window.addEventListener("wheel", e => e.preventDefault(), {
passive: false
});
let main = document.querySelector(".main-nav");
main.addEventListener("wheel", (event) => {
event.preventDefault();
let ct = event.currentTarget;
let canScroll = !!ct.dataset.canscroll;
if (!canScroll) {
return false;
}
let w = ct.scrollWidth - ct.clientWidth;
let sleft = Math.trunc(ct.scrollLeft);
let wd = event.wheelDelta;
scrollContainer(ct, wd < 0);
if (sleft == 0 || sleft == w) {
ct.dataset.canscroll = "true";
}
}, {
passive: true
});
main.addEventListener("scrollend", (event) => {
let ct = event.currentTarget;
ct.dataset.canscroll = "true";
});
main.addEventListener("scroll", (event) => {
// fires a lot as it scrolls.
let canScroll = !!event.currentTarget.dataset.canscroll;
if (canScroll) {
return false;
}
});
function scrollContainer(main, plus) {
main.dataset.canscroll = "false";
let scroll = plus ? main.clientWidth : -main.clientWidth;
main.scrollBy({
left: scroll,
behavior: 'smooth'
});
}
* {
overflow-y: hidden;
box-sizing: border-box;
font-size: 16px;
margin: 0;
padding: 0;
}
/* hide the scroll bar w/these two */
.main-nav {
overflow-x: scroll;
scrollbar-width: none;
-ms-overflow-style: none;
}
.main-nav::-webkit-scrollbar {
width: 0;
height: 0;
}
.main-nav {
display: flex;
flex-direction: row;
height: 100vh;
scroll-snap-type: x mandatory;
overflow-x: scroll;
scroll-behavior: smooth;
width: inherit;
}
.main-nav[data-canscroll="true"] {
pointer-events: auto;
}
.main-nav[data-canscroll="false"] {
pointer-events: none;
}
.a {
display: grid;
place-items: center;
text-align: center;
border: 1px solid yellow;
}
.a:nth-child(even) {
background: blue;
}
.a:nth-child(odd) {
background: red;
}
.a {
min-width: 100%;
height: 100%;
scroll-snap-align: center;
scroll-snap-stop: always;
color: white;
font-size: 3em;
}
.scrollR {
padding: 1em;
}
<div class="main-nav" data-canscroll="true">
<div class="a">first ONE</div>
<div class="a">TWO</div>
<div class="a">THREE</div>
<div class="a">FOUR</div>
<div class="a">five</div>
<div class="a">six</div>
<div class="a">last one</div>
</div>
Answered By - Mark Schultheiss
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.