I added this code to my WordPress based website to make its front page horizontal. But it's not smooth and I cannot add scroll snap or anchor points. Can you help me about these? My website is https://kozb.art
<script type="text/javascript">
function replaceVerticalScrollByHorizontal( event ) {
if ( event.deltaY !== 0 ) {
window.scroll(window.scrollX + event.deltaY * 2, window.scrollY );
event.preventDefault();
}
}
const mediaQuery = window.matchMedia( '(min-width: 770px)' );
if ( mediaQuery.matches ) {
window.addEventListener( 'wheel', replaceVerticalScrollByHorizontal );
}
</script>
Edit: Here's my CSS code to make front page horizontal:
.elementor-section-wrap{
display: inline-flex;
}
.elementor-section{
width:100vw;
}
body{
overflow-y: hidden;
overscroll-behavior-y: none;
}
@media (max-width:768px){
.elementor-section-wrap{
display: block;
}
body{
overflow-y: auto;
overflow-x: hidden;
overscroll-behavior-x: none;
}
}
You need animation knowledge to move the horizontal scroll smoothly. Let's start with a horizontal scrolling environment.
// index.html
...
<head>
<link href="index.css" rel="stylesheet">
</head>
<body>
<div id="screen">
<div id="content1"></div><div id="content2"></div><div id="content3"></div><div id="content4"></div>
</div>
<script src="./main.js"></script>
</body>
...
/* index.css */
body {
margin: 0;
width: 100vw;
height: 100vh;
overflow-y: hidden;
}
#screen {
white-space: nowrap;
height: 100%;
}
#screen > div {
width: 100vw;
height: 100vh;
display: inline-block;
}
#screen > div:nth-child(1) {
background: aqua;
}
#screen > div:nth-child(2) {
background: blueviolet
}
#screen > div:nth-child(3) {
background: chocolate
}
#screen > div:nth-child(4) {
background: darkolivegreen;
}
A web page has now been created that has four sections and occupies one screen size per section. For smooth, snap horizontal scroll applications, let's think step by step about what we need to make code for animation.
To implement Snap, you need to know what value scroll X should move to. In the current layout, the value is the offsetLeft value of the section element. And the section size changes depending on the browser size. So the code to get the offsetLeft of the section can be created as follows:
let sectionAnchorPointer = [];
const resizeHandler = () => {
const content1 = document.getElementById('content1');
const content2 = document.getElementById('content2');
const content3 = document.getElementById('content3');
const content4 = document.getElementById('content4');
sectionAnchorPointer = [content1.offsetLeft, content2.offsetLeft, content3.offsetLeft, content4.offsetLeft];
};
addEventListener('resize', resizeHandler);
addEventListener('DOMContentLoaded', () => {
resizeHandler();
});
In order to store the section offsetLeft value from the beginning, a function was executed to update the section offsetLeft when DOMContentLoaded occurred. If you want to make it more efficient, please apply debounce to the resize event handler.
Next, when the wheel occurs, find the section to move. In order to find the section to be moved, it is necessary to determine where the section is located and then perform the calculation according to the direction of the wheel. The code is as follows:
let nextSectionIndex = 0;
const getCurrentSectionIndex = () => sectionAnchorPointer.findIndex((leftValue, i, array) => {
const scrollX = Math.ceil(window.scrollX); // Fixed a bug where scrollX was decimalized
const rightValue = array[i + 1] ?? Infinity;
return leftValue <= scrollX && scrollX < rightValue;
});
window.addEventListener('wheel', ({ deltaY }) => {
const currentSectionIndex = getCurrentSectionIndex();
const add = Math.abs(deltaY) / deltaY;
nextSectionIndex = currentSectionIndex + add;
nextSectionIndex = Math.min(sectionAnchorPointer.length - 1, Math.max(0, nextSectionIndex)); // To avoid pointing to a section index that does not exist
console.log(sectionAnchorPointer[nextSectionIndex]);
});
To save the scroll position when accessing the page, Call the function when a DOMContentLoaded event occurs.
...
addEventListener('DOMContentLoaded', () => {
resizeHandler();
nextSectionIndex = getCurrentSectionIndex();
});
...
Next, apply the animation so that it slowly changes to the offsetLeft value of the section that needs to move the current scrollX value. For ease of understanding, let's make it a linear animation without acceleration. The code is as follows:
const SCROLL_SPEED = 70; // It can be changed for speed control.
requestAnimationFrame(function scroll() {
const nextScrollX = sectionAnchorPointer[nextSectionIndex];
// linear animtion
if (Math.abs(window.scrollX - nextScrollX) > SCROLL_SPEED) {
const val = -Math.abs(window.scrollX - nextScrollX) / (window.scrollX - nextScrollX);
window.scroll(window.scrollX + val * SCROLL_SPEED, window.scrollY);
} else {
window.scroll(nextScrollX, window.scrollY);
}
requestAnimationFrame(scroll);
});
To apply dynamic animation by adding acceleration, add the following code instead of the code above.
requestAnimationFrame(function scroll() {
const nextScrollX = sectionAnchorPointer[nextSectionIndex];
// curve animation
if (Math.abs(window.scrollX - nextScrollX) > 1) {
let val = (nextScrollX - window.scrollX) / 8; // You can change 8 to another value to adjust the animation speed.
val = val > 0 ? Math.max(val, 1) : Math.min(val, -1);
window.scroll(window.scrollX + val, window.scrollY);
} else {
window.scroll(nextScrollX, window.scrollY);
}
requestAnimationFrame(scroll);
});
This is a simple example of implementation for understanding. Here is a link to the project using the code. If you are interested, please refer to the following link to understand Javascript animation. For your information, it would be more convenient to make an animation using a known library such as anime.js or popmotion.
This is the script code that fits your structure. Remove the existing wheel listener and insert this content.
const wrap = document.querySelectorAll('.elementor-section-wrap')[1]
let sectionAnchorPointer = [];
let resultX = 0;
const resizeHandler = () => {
sectionAnchorPointer = [...new Set([...wrap.children].map(el => el.offsetLeft))];
};
addEventListener('resize', resizeHandler);
addEventListener('DOMContentLoaded', () => {
resizeHandler();
nextSectionIndex = getCurrentSectionIndex();
});
resizeHandler();
let nextSectionIndex = 0;
const getCurrentSectionIndex = () => sectionAnchorPointer.findIndex((leftValue, i, array) => {
const scrollX = Math.ceil(resultX); // Fixed a bug where scrollX was decimalized
const rightValue = array[i + 1] ?? Infinity;
return leftValue <= resultX && resultX < rightValue;
});
window.addEventListener('wheel', (ev) => {
const {deltaY} = ev;
const currentSectionIndex = getCurrentSectionIndex();
const add = Math.abs(deltaY) / deltaY;
nextSectionIndex = currentSectionIndex + add;
nextSectionIndex = Math.min(sectionAnchorPointer.length - 1, Math.max(0, nextSectionIndex)); // To avoid pointing to a section index that does not exist
console.log(sectionAnchorPointer[nextSectionIndex]);
});
const SCROLL_SPEED = 70; // It can be changed for speed control.
requestAnimationFrame(function scroll() {
const nextScrollX = sectionAnchorPointer[nextSectionIndex];
// linear animtion
if (Math.abs(resultX - nextScrollX) > SCROLL_SPEED) {
const val = -Math.abs(resultX - nextScrollX) / (resultX - nextScrollX);
resultX = resultX + val * SCROLL_SPEED;
} else {
resultX = nextScrollX;
}
window.scroll(resultX , 0);
requestAnimationFrame(scroll);
});