全屏切换页面的核心是CSS viewport与JavaScript事件控制结合,需用100vh/min-height:100dvh、overflow:hidden、scroll-behavior:smooth,并配合scrollIntoView精准锚定、preventDefault阻止默认滚动、IntersectionObserver检测激活页及规避移动端兼容性问题。
直接用 height: 100vh 不够,必须配合 overflow: hidden 和 scroll-behavior: smooth(可选),否则滚动会“漏出”或跳变。移动端尤其要注意 vh 在 Safari 中的缩放 bug——页面放大时 100vh 会小于可视区,推荐用 min-height: 100dvh(现代浏览器)或回退到 100vh。
结构上建议用单层 <section> 平铺,每个 section 高度设为 100vh,不嵌套滚动容器;否则 touchmove 会被子元素拦截,导致滑动失效。
scrollIntoView 实现平滑切换(兼容性好、无依赖)不用轮子,靠原生 API 就能完成。关键点不是“动画”,而是“精准锚定”和“阻止默认滚动干扰”:
section.scrollIntoView({ behavior: 'smooth', block: 'start' }) 是基础,但必须确保调用前页面没在滚动中,否则行为不可预测wheel 或 touchmove 时,要 e.preventDefault(),否则系统滚动会和你的切换冲突touchstart/touchend 记录位移差,仅靠 wheel 在 iOS 上不触发isScrolling 标志位,scrollend 事件(或 setTimeout 延迟)后重置示例片段:
sections.forEach((sec, i) => { sec.addEventListener('click', () => { if (isScrolling) return; isScrolling = true; sec.scrollIntoView({ behavior: 'smooth' }); });});
IntersectionObserver 检测当前页并更新导航状态单纯切换不够,用户需要知道“我在哪一页”。用 IntersectionObserver 监听各 section 的可见比例,比监听 scroll 事件更轻量、更准确:
立即学习“前端免费学习笔记(深入)”;
[0.2, 0.5, 0.8],避免临界抖动;只在 entry.intersectionRatio > 0.5 时认为该页“激活”callback 里直接操作 DOM 样式,改用 class 切换,让 CSS 控制高亮observer.takeRecords() 或初始化时检查 getBoundingClientRect()
常见错误:rootMargin: '0px' 导致首屏 section 在刚加载时 intersectionRatio = 0 —— 改成 '-1px' 或 '0px 0px -1px 0px' 可修复。
90% 的“滑不动”问题出在以下三个地方,而不是 JS 性能:
transform: translateZ(0) 或 will-change: transform 却没配 backface-visibility: hidden,导致 iOS 渲染层撕裂input、video 或 iframe,它们会劫持 touch 事件,需加 pointer-events: none(内容区域除外)body 或 html 上设置了 overscroll-behavior: contain,但没同步加到各 section 上,导致滑动到边界时整个页面晃动真正难调的是 Safari 的 dvh 支持不一致和微信内置浏览器对 scroll-behavior 的部分忽略——这种时候老老实实用 window.scrollTo({ top: target.offsetTop }) + CSS @media (prefers-reduced-motion) 降级更稳。