ARTICLE AD BOX
I think, you can use the lerp function. As long as we have in our minds the same image jumping or its wildness on the scroll...
EDIT
And generally speaking, if I were you, wherever you have moving elements that depend on an event (and you don't want to deal with calculating a custom scrollbar), I would use Lenis or another library that will smooth the movement...
document.addEventListener('DOMContentLoaded', function() {
const sections = document.querySelectorAll('#parallax');
if (!sections.length) return;
// =========================
// LENIS
// =========================
const lenis = typeof Lenis !== 'undefined' ?
new Lenis({
duration: 1.2,
smoothWheel: true,
smoothTouch: false,
wheelMultiplier: 1,
touchMultiplier: 1.5
}) :
null;
// =========================
// CONFIG
// =========================
const maxOffset = 900;
const maxZoom = 1.4;
const smoothing = 0.06;
// smooth only after scroll
let hasScrolled = false;
// =========================
// HELPERS
// =========================
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function lerp(start, end, factor) {
return start + (end - start) * factor;
}
// =========================
// STATE
// =========================
const state = new WeakMap();
sections.forEach(section => {
state.set(section, {
currentY: 0,
targetY: 0,
currentScale: 1,
targetScale: 1,
initialized: false
});
});
// =========================
// PARALLAX
// =========================
function calculateTargets(section) {
const rect = section.getBoundingClientRect();
const windowHeight = window.innerHeight;
const sectionHeight = rect.height;
const totalTravel = windowHeight + sectionHeight;
const traveled = windowHeight - rect.top;
let progress = traveled / totalTravel;
progress = clamp(progress, 0, 1);
const eased = Math.pow(progress, 1.4);
return {
y: eased * maxOffset,
scale: 1 + eased * maxZoom
};
}
function animate(time) {
if (lenis) {
lenis.raf(time);
}
sections.forEach(section => {
const s = state.get(section);
const targets = calculateTargets(section);
s.targetY = targets.y;
s.targetScale = targets.scale;
// ====================================
// FIRST LOAD: No animation
// ====================================
if (!s.initialized) {
s.currentY = s.targetY;
s.currentScale = s.targetScale;
s.initialized = true;
}
// ====================================
// smooth after scroll
// ====================================
else if (hasScrolled) {
s.currentY = lerp(
s.currentY,
s.targetY,
smoothing
);
s.currentScale = lerp(
s.currentScale,
s.targetScale,
smoothing
);
}
// ====================================
// APPLY
// ====================================
section.style.setProperty(
'--parallax-offset',
`${s.currentY.toFixed(2)}px`
);
section.style.setProperty(
'--parallax-scale',
s.currentScale.toFixed(4)
);
});
requestAnimationFrame(animate);
}
// =========================
// DETECT FIRST SCROLL
// =========================
function activateSmooth() {
hasScrolled = true;
window.removeEventListener('wheel', activateSmooth);
window.removeEventListener('touchmove', activateSmooth);
window.removeEventListener('scroll', activateSmooth);
}
window.addEventListener('wheel', activateSmooth, {
passive: true
});
window.addEventListener('touchmove', activateSmooth, {
passive: true
});
window.addEventListener('scroll', activateSmooth, {
passive: true
});
// =========================
// START
// =========================
requestAnimationFrame(animate);
});
//smooth scroll rule from here:
document.addEventListener('DOMContentLoaded', function() {
const header = document.querySelector('header');
function getHeaderHeight() {
return header ? header.offsetHeight : 0;
}
function smoothScrollTo(target, duration) {
const start = window.pageYOffset;
const headerOffset = getHeaderHeight();
const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - headerOffset;
const distance = targetPosition - start;
let startTime = null;
function easeInOutQuad(t) {
return t < 0.5 ?
2 * t * t :
1 - Math.pow(-2 * t + 2, 2) / 2;
}
function animation(currentTime) {
if (startTime === null) startTime = currentTime;
const timeElapsed = currentTime - startTime;
const progress = Math.min(timeElapsed / duration, 1);
const ease = easeInOutQuad(progress);
window.scrollTo(0, start + distance * ease);
if (timeElapsed < duration) {
requestAnimationFrame(animation);
}
}
requestAnimationFrame(animation);
}
document.querySelectorAll('a[href*="#"]:not(.popup-open)').forEach(function(anchor) {
anchor.addEventListener('click', function(e) {
const href = this.getAttribute('href');
const hashIndex = href.indexOf('#');
if (hashIndex === -1) return;
const targetId = href.substring(hashIndex);
if (!targetId || targetId === '#') return;
const target = document.querySelector(targetId);
if (target) {
e.preventDefault();
smoothScrollTo(target, 2000);
}
});
});
});
html {
scroll-behavior: auto; /*to disable default scroll behavior*/
}
#parallax {
position: relative;
overflow: hidden;
--parallax-offset: 0px;
--parallax-scale: 1;
}
#parallax::before {
content: "";
position: absolute;
top: -60%;
left: 0%;
width: 80%;
height: 76%;
background-image: url('https://i.sstatic.net/Cb72ZEgr.jpg');
background-size: cover;
background-position: center 45%;
background-repeat: no-repeat;
transform: translate3d(0, var(--parallax-offset), 0) scale(var(--parallax-scale));
transform-origin: 50% 2%;
will-change: transform;
z-index: 0;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
contain: paint;
}
#parallax>.e-con-inner {
position: relative;
z-index: 2;
}
@media (min-width: 1530px) {
#parallax::before {
top: -70%;
width: 80%;
height: 86%;
transform-origin: 50% 2%;
}
}
@media (min-width: 580px) and (max-height: 800px) {
#parallax::before {
background-image: url('https://i.sstatic.net/EDudN3ZP.jpg');
top: -80%;
width: 100%;
height: 100%;
transform-origin: 50% 10%;
background-size: contain;
}
}
@media (min-width: 1900px) {
#parallax::before {
background-image: url('https://i.sstatic.net/JpACme02.jpg');
background-position: center 0%;
top: -136%;
width: 100%;
height: 112%;
transform-origin: 50% 10%;
background-size: contain;
}
}
@media (min-width: 1900px) and (max-height: 1000px) {
#parallax::before {
background-image: url('https://i.sstatic.net/EDudN3ZP.jpg');
background-position: center 0%;
top: -100%;
width: 100%;
height: 102%;
transform-origin: 50% 10%;
background-size: contain;
}
}
@media (max-width: 1024px) {
#parallax::before {
top: -80%;
}
}
@media (min-width: 360px) and (max-width: 767px) {
#parallax::before {
top: -50%;
left: 10%;
}
}
#parallax::before {
transform:
translate3d(0, var(--parallax-offset), 0) scale3d(var(--parallax-scale), var(--parallax-scale), 1);
will-change: transform;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
transform-style: preserve-3d;
contain: paint;
}
html.lenis,
html.lenis body {
height: auto;
}
.lenis.lenis-smooth {
scroll-behavior: auto !important;
}
.lenis.lenis-smooth [data-lenis-prevent] {
overscroll-behavior: contain;
}
.lenis.lenis-stopped {
overflow: hidden;
}
<script src="https://unpkg.com/[email protected]/dist/lenis.min.js"></script>
<a href="#footer">to footer</a>
<div class="elementor-element elementor-element-2d33ef6 e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no e-con e-parent e-lazyloaded" data-id="2d33ef6" data-element_type="container" data-e-type="container" id="parallax">
<div class="e-con-inner">
<div class="elementor-element elementor-element-afd013c e-con-full e-flex wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no e-con e-child" data-id="afd013c" data-element_type="container" data-e-type="container">
<div class="elementor-element elementor-element-de7cf26 e-con-full spacearound e-flex wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-equal-height-no e-con e-child" data-id="de7cf26" data-element_type="container" data-e-type="container" data-settings="{"background_background":"classic"}">
<div class="elementor-element elementor-element-78b633b elementor-widget elementor-widget-text-editor" data-id="78b633b" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
<h1 style="text-align: center; font-size: 60px;">Lorem ipsum</h1>
</div>
<div>
<h2 style="text-align: center; font-weight: 300;"><span style="color: inherit; font-family: inherit; font-size: 2rem;">Lorem ipsum dolor sit </span></h2>
</div>
<div>
<h2 style="text-align: center;"><img src="https://i.sstatic.net/O9FcKWC1.png" style="width: 60px; height: 60px"alt="logo"></h2>
</div>
<div>
<p style="text-align: center; font-size: 26px;"><strong>amet consectetur adipiscing elit</strong></p>
</div>
<div class="elementor-element elementor-element-f6da976 elementor-align-center elementor-widget elementor-widget-button" data-id="f6da976" data-element_type="widget" data-e-type="widget" data-widget_type="button.default">
</div>
</div>
</div>
</div>
</div>
<div>
<p>
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
</p>
<div id="footer"></div>
</div>
