如何实现整站文档的惯性平滑滚动(纯JS/HTML/CSS实现)
原生实现全站惯性平滑滚动方案
你要的滚轮滚动全程顺滑、停止带缓动延迟的效果,通过拦截原生滚动+requestAnimationFrame自定义缓动动画即可实现,html { scroll-behavior: smooth; } 只能处理锚点跳转的平滑,做不到惯性缓停,下面是完整可直接用的无依赖实现:
核心实现逻辑
- 禁用浏览器原生滚轮滚动行为,避免和自定义动画冲突
- 用线性插值算法逐帧计算滚动位置,实现启动柔和、减速缓停的惯性手感
- 自动适配页面内容高度,处理滚动边界,不会出现超出页面范围的问题
- 兼容鼠标滚轮、触摸板双指滚动场景
完整CSS代码
* { margin: 0; padding: 0; box-sizing: border-box; } html { scroll-behavior: auto !important; overflow: hidden; } .scroll-container { width: 100%; height: 100vh; overflow-y: auto; overflow-x: hidden; /* 隐藏原生滚动条,需要自定义滚动条可删除下面两行 */ scrollbar-width: none; } .scroll-container::-webkit-scrollbar { display: none; }
完整JS代码
class SmoothScroll { constructor(options = {}) { // 可自定义手感参数 this.config = { speed: options.speed ?? 0.85, // 滚动速度系数,越大滚动越快 ease: options.ease ?? 0.08, // 缓动系数,越小启动/停止越柔和 threshold: options.threshold ?? 0.15 // 动画停止判定阈值 } this.currentScroll = 0 this.targetScroll = 0 this.container = null this.rafId = null this.init() } init() { // 创建滚动容器,自动包裹页面原有内容 this.container = document.createElement('div') this.container.className = 'scroll-container' while(document.body.firstChild) { this.container.appendChild(document.body.firstChild) } document.body.appendChild(this.container) this.bindEvents() this.animationLoop() } bindEvents() { // 拦截滚轮事件 window.addEventListener('wheel', (e) => { e.preventDefault() this.targetScroll += e.deltaY * this.config.speed // 限制滚动边界 const maxScroll = this.container.scrollHeight - window.innerHeight this.targetScroll = Math.max(0, Math.min(this.targetScroll, maxScroll)) }, { passive: false }) // 窗口resize时重新校准边界 window.addEventListener('resize', () => { const maxScroll = this.container.scrollHeight - window.innerHeight this.targetScroll = Math.max(0, Math.min(this.targetScroll, maxScroll)) this.currentScroll = Math.max(0, Math.min(this.currentScroll, maxScroll)) }) } animationLoop() { // 线性插值计算当前滚动位置,实现缓动效果 this.currentScroll += (this.targetScroll - this.currentScroll) * this.config.ease // 接近目标位置时直接归位,避免微小抖动 if(Math.abs(this.targetScroll - this.currentScroll) < this.config.threshold) { this.currentScroll = this.targetScroll } this.container.scrollTop = this.currentScroll this.rafId = requestAnimationFrame(() => this.animationLoop()) } // 对外暴露手动滚动方法,兼容锚点跳转、回到顶部这类需求 scrollTo(targetY, isInstant = false) { this.targetScroll = targetY if(isInstant) this.currentScroll = targetY } destroy() { cancelAnimationFrame(this.rafId) } } // DOM加载完成后自动初始化 document.addEventListener('DOMContentLoaded', () => { window.smoothScroll = new SmoothScroll({ speed: 0.9, ease: 0.075 }) })
使用说明
- 把上述CSS和JS加入页面即可直接生效,不需要改动原有页面结构,脚本会自动把所有页面内容移入滚动容器
- 可以通过调整
SmoothScroll初始化时的参数匹配你想要的手感:ease值调小会让滚动更柔和、停止延迟更长,调大则手感更跟手 - 如果需要显示自定义滚动条,删掉CSS里隐藏滚动条的代码,自行给
.scroll-container写滚动条样式即可 - 页面动态加载内容(比如懒加载、无限滚动)时不需要额外调用API,脚本会自动识别新的内容高度,滚动边界会自动适配
- 所有现代浏览器都兼容,不需要polyfill
内容的提问来源于stack exchange,提问作者Pavel
相关产品推荐
相关产品推荐

