如何让背景视差在滚动条控件上更平滑?
当前,视差背景滚动在使用触控板滚动和滚动条移动时显得有点“卡顿”,但通过锚点点击时却很好且平滑。
当我通过鼠标滚轮滚动时,滚动并不平滑。如果慢慢拖动滚动条,图像滚动也不平滑。如果快速拖动滚动条,我就看不出卡顿,似乎很顺滑。通过点击垂直滚动条上下方的箭头按钮进行滚动时也会有卡顿。
正如Abra所说。
以下是我的代码:
document.addEventListener('DOMContentLoaded', function() {
const sections = document.querySelectorAll('#parallax');
const maxOffset = 900; // the final state: 400px down. "400px" is a refference for me how to adjust the code for differing screen sizes. Not part of the logic
const maxZoom = 1.4; // the lower the smaller the zoom
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function updateParallax() {
const windowHeight = window.innerHeight;
sections.forEach(section => {
const rect = section.getBoundingClientRect();
const sectionHeight = rect.height;
/*
The total scroll range:
starts: when the bottom of the section reaches the bottom of the viewport
ends: when the top of the section reaches the top of the viewport
*/
const totalTravel = windowHeight + sectionHeight;
// how much it travelled on the "way"
const traveled = windowHeight - rect.top;
let progress = traveled / totalTravel;
progress = clamp(progress, 0, 1);
// Finer movement
const eased = Math.pow(progress, 1.4);
// 0 -> 400px down
const y = eased * maxOffset;
// 1 -> 1.4 zoom
const scale = 1 + eased * maxZoom;
section.style.setProperty('--parallax-offset', `${y}px`);
section.style.setProperty('--parallax-scale', scale);
});
}
let ticking = false;
function onScroll() {
if (!ticking) {
requestAnimationFrame(() => {
updateParallax();
ticking = false;
});
ticking = true;
}
}
window.addEventListener('scroll', onScroll, {
passive: true
});
window.addEventListener('resize', onScroll);
updateParallax();
});
//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;
/* min-height: 100vh; */
--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%;
}
}
<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>
<div id="footer"></div>
</div>
可在此处查看站点实际效果:https://oryxelconsulting.com/
在站点上看起来比这里的SO更好。
解决方案
我认为,你可以使用 lerp 函数。只要我们对同一张图片在滚动时的跳动或它的 野性 有共识……
编辑
一般来说,换作是我,在任何需要基于事件移动的元素的地方(你又不想自己处理自定义滚动条的计算),我都会使用Lenis或其他可以平滑 移动 的库来实现平滑效果……
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.5;
// 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>
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。