纯Vanilla JS实现指定区块垂直平滑滚动吸附效果求助
垂直全屏滚动吸附问题修复
问题背景
需要实现垂直全屏滚动吸附效果:鼠标滚轮或键盘上下键触发时,平滑滚动到上/下一个带data-section属性的区块(仅前3个section应用该效果),剩余section保持正常滚动。现有代码无法实现预期的立即吸附滚动效果。
现有代码的核心问题
- 未阻止原生滚动行为,导致自定义滚动与浏览器默认滚动冲突
- 未处理键盘上下键触发逻辑
- 依赖固定窗口高度计算偏移,未考虑元素实际位置,窗口尺寸变化后会失效
- 无滚动锁机制,连续滚动会触发多次动画
- 未判断滚动范围,进入非目标区块后仍会强制吸附
修正后的完整实现
1. 必要的CSS设置
确保带data-section的区块占满视口:
html, body { margin: 0; padding: 0; scroll-behavior: smooth; /* 基础平滑滚动 */ } [data-section] { height: 100vh; margin: 0; display: flex; flex-direction: column; justify-content: center; align-items: center; /* 可选:添加背景区分区块 */ background: #f5f5f5; } [data-section]:nth-child(2) { background: #e8e8e8; } [data-section]:nth-child(3) { background: #dcdcdc; }
2. 修复后的JavaScript代码
const sections = document.querySelectorAll('[data-section]'); let currentIndex = 0; let isScrolling = false; const scrollLockTimeout = 800; // 滚动动画锁定时长,匹配平滑滚动时间 // 获取每个目标区块的顶部偏移量 function getSectionOffsets() { return Array.from(sections).map(section => section.offsetTop); } let sectionOffsets = getSectionOffsets(); // 窗口resize时更新偏移量 window.addEventListener('resize', () => { sectionOffsets = getSectionOffsets(); }); // 处理滚动逻辑 function handleScroll(direction) { if (isScrolling) return; isScrolling = true; // 计算目标索引 const targetIndex = direction === 'down' ? currentIndex + 1 : currentIndex - 1; // 检查目标索引是否在有效范围内 if (targetIndex >= 0 && targetIndex < sections.length) { window.scrollTo({ top: sectionOffsets[targetIndex], behavior: 'smooth' }); currentIndex = targetIndex; } else { // 超出目标区块范围,释放滚动锁,恢复正常滚动 isScrolling = false; return; } // 滚动完成后解锁 setTimeout(() => { isScrolling = false; }, scrollLockTimeout); } // 监听鼠标滚轮事件 window.addEventListener('wheel', (e) => { const currentScrollTop = window.pageYOffset; const lastSectionBottom = sectionOffsets[sections.length - 1] + sections[sections.length - 1].offsetHeight; // 如果不在目标区块范围内,不阻止默认行为 if (currentScrollTop < sectionOffsets[0] || currentScrollTop >= lastSectionBottom) { return; } e.preventDefault(); const direction = e.deltaY > 0 ? 'down' : 'up'; handleScroll(direction); }, { passive: false }); // 监听键盘上下键事件 window.addEventListener('keydown', (e) => { const currentScrollTop = window.pageYOffset; const lastSectionBottom = sectionOffsets[sections.length - 1] + sections[sections.length - 1].offsetHeight; if (currentScrollTop < sectionOffsets[0] || currentScrollTop >= lastSectionBottom) { return; } if (e.key === 'ArrowDown') { e.preventDefault(); handleScroll('down'); } else if (e.key === 'ArrowUp') { e.preventDefault(); handleScroll('up'); } });
3. 关键改进点
- 阻止默认滚动冲突:在目标区块范围内阻止原生滚轮事件,避免滚动混乱
- 动态偏移计算:使用元素实际
offsetTop计算滚动位置,适配窗口尺寸变化 - 滚动锁机制:防止连续滚动触发多次动画,确保每次滚动完成后才响应下一次操作
- 范围判断:当滚动到目标区块之外时,自动恢复正常滚动行为
- 支持键盘控制:添加上下箭头键触发滚动的逻辑
内容的提问来源于stack exchange,提问作者BizMAN
相关产品推荐
相关产品推荐

