🎯 案例核心效果
- 页面展示导航栏和多个锚点区块,点击导航按钮可平滑滚动到对应区块;
- 滚动过程带有缓动动画,避免生硬跳转,交互体验流畅;
- 支持自定义滚动速度,适配不同场景的交互需求;
- 滚动时导航栏选中状态自动同步,视觉反馈清晰;
- 代码精简可复用,支持直接集成到任意页面,适配 PC 和移动端。

📌 实现思路(极简清晰)
分 3 步落地,逻辑简单易上手,零基础也能快速掌握:
- HTML :搭建导航栏和锚点区块,通过
id和自定义属性绑定对应关系; - CSS:美化导航栏和区块样式,突出选中状态,保证页面布局美观;
- JS:监听导航点击事件→获取目标区块位置→封装动画滚动函数→同步导航选中状态→监听滚动事件更新选中状态。
🚀 完整源码(可直接复制运行)
html
预览
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS 平滑滚动到指定位置 | 实战案例</title>
<style>
/* 全局重置(精简规范) */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei", sans-serif;
}
/* 导航栏样式:固定顶部,吸顶效果 */
.nav {
position: fixed;
top: 0;
left: 0;
width: 100%;
display: flex;
justify-content: center;
gap: 20px;
padding: 15px 0;
background-color: #fff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
z-index: 999;
}
/* 导航按钮样式 */
.nav-btn {
padding: 8px 16px;
font-size: 16px;
color: #666;
background: none;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
}
.nav-btn:hover {
color: #409eff;
background-color: #f5f7fa;
}
.nav-btn.active {
color: #fff;
background-color: #409eff;
}
/* 内容容器:适配导航栏高度 */
.content {
margin-top: 70px;
}
/* 锚点区块样式 */
.section {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-size: 32px;
color: #333;
}
/* 不同区块区分色 */
#section1 { background-color: #f0f9ff; }
#section2 { background-color: #fdf2f8; }
#section3 { background-color: #f5f5f5; }
#section4 { background-color: #f7f3e9; }
/* 移动端适配:调整导航按钮大小和间距 */
@media (max-width: 500px) {
.nav {
gap: 10px;
padding: 12px 0;
}
.nav-btn {
padding: 6px 12px;
font-size: 14px;
}
.section {
font-size: 24px;
}
}
</style>
</head>
<body>
<!-- 导航栏 -->
<div class="nav">
<button class="nav-btn active" data-target="section1">区块1</button>
<button class="nav-btn" data-target="section2">区块2</button>
<button class="nav-btn" data-target="section3">区块3</button>
<button class="nav-btn" data-target="section4">区块4</button>
</div>
<!-- 内容区块 -->
<div class="content">
<div class="section" id="section1">区块 1 内容</div>
<div class="section" id="section2">区块 2 内容</div>
<div class="section" id="section3">区块 3 内容</div>
<div class="section" id="section4">区块 4 内容</div>
</div>
<script>
// 1. 获取DOM元素
const navBtns = document.querySelectorAll('.nav-btn');
const sections = document.querySelectorAll('.section');
// 滚动速度(数值越小越快,建议10-30之间)
const scrollSpeed = 20;
// 2. 封装平滑滚动函数(核心动画逻辑)
function smoothScrollTo(targetId) {
// 获取目标元素
const targetElement = document.getElementById(targetId);
if (!targetElement) return;
// 获取目标元素的顶部位置(减去导航栏高度,避免被遮挡)
const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - 0;
// 当前滚动位置
let currentPosition = window.pageYOffset;
// 计算滚动步长(缓动效果)
const distance = targetPosition - currentPosition;
let step;
// 开启动画帧循环
const scrollAnimation = () => {
// 计算每一步的滚动距离(缓动:距离越近,步长越小)
step = distance / scrollSpeed;
// 判断是否到达目标位置
if (Math.abs(step) < 1) {
window.scrollTo(0, targetPosition);
return;
}
// 滚动一步
window.scrollTo(0, currentPosition + step);
currentPosition += step;
// 继续下一帧动画
requestAnimationFrame(scrollAnimation);
};
// 启动滚动动画
requestAnimationFrame(scrollAnimation);
}
// 3. 绑定导航按钮点击事件
navBtns.forEach(btn => {
btn.addEventListener('click', () => {
// 获取目标区块ID
const targetId = btn.dataset.target;
// 执行平滑滚动
smoothScrollTo(targetId);
// 更新导航选中状态
updateNavActive(targetId);
});
});
// 4. 封装更新导航选中状态函数
function updateNavActive(targetId) {
navBtns.forEach(btn => {
btn.classList.toggle('active', btn.dataset.target === targetId);
});
}
// 5. 监听页面滚动,自动更新导航选中状态
window.addEventListener('scroll', () => {
// 获取当前滚动位置
const scrollPosition = window.pageYOffset + window.innerHeight / 2;
// 遍历所有区块,判断当前可视区域的区块
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionBottom = sectionTop + section.offsetHeight;
// 当前区块在可视区域中间
if (scrollPosition >= sectionTop && scrollPosition < sectionBottom) {
updateNavActive(section.id);
}
});
});
</script>
</body>
</html>
📖 核心代码拆解(重点突出,易懂好记)
1. HTML 结构(极简规整)
核心分为 2 部分,绑定关系清晰:
nav:导航栏容器,按钮通过data-target属性绑定对应区块的id,实现关联;content:锚点区块容器,每个区块有唯一id,与导航按钮一一对应。
2. CSS 样式(简约美观,交互友好)
重点关注 3 个核心样式:
- 吸顶导航 :导航栏设置
position: fixed,固定在页面顶部,方便随时点击; - 选中状态 :导航按钮通过
active类名区分选中状态,视觉反馈清晰; - 区块布局 :每个区块设置
height: 100vh,占满视口高度,适配滚动效果。
3. JS 逻辑(核心中的核心,分步拆解)
✅ 平滑滚动函数:
- 核心使用
requestAnimationFrame实现帧动画,相比setInterval更流畅、性能更好; - 计算目标位置时减去导航栏高度,避免区块内容被导航遮挡;
- 缓动效果:滚动步长随距离缩小而减小,实现自然的减速滚动;
✅ 导航事件绑定 :点击按钮通过data-target获取目标区块 ID,触发滚动并更新选中状态;
✅ 滚动监听:页面滚动时自动判断当前可视区块,同步更新导航选中状态,提升交互完整性;
✅ 参数可配 :scrollSpeed变量可自定义滚动速度,适配不同场景需求。
💡 新手常见踩坑点(精准避坑,少走弯路)
- 滚动后区块被导航遮挡? → 计算目标位置时未减去导航栏高度,需在
targetPosition中减去导航栏高度(示例中因导航栏高度已通过margin-top抵消,故设为 0); - 滚动动画卡顿? → 使用
setInterval代替requestAnimationFrame,后者是浏览器原生动画 API,更适配屏幕刷新率; - 选中状态不同步? → 滚动监听时判断条件不合理,需以可视区域中间位置(
window.innerHeight / 2)为基准,而非顶部; - 移动端滚动异常? → 未考虑视口高度变化,区块使用
100vh而非固定高度,适配不同屏幕; - 滚动到目标位置不精准? → 步长计算未做边界判断,需在
scrollAnimation中判断Math.abs(step) < 1时直接定位到目标位置。
🔧 扩展思路(落地性强,新手可练)
- 滚动速度适配:根据滚动距离动态调整速度,长距离滚动更快,短距离更慢;
- 滚动禁用:添加开关,滚动过程中禁用导航按钮点击,避免重复触发;
- 回到顶部按钮:添加回到页面顶部的按钮,复用平滑滚动函数;
- 滚动进度条:添加页面滚动进度条,实时显示当前滚动位置占总长度的比例;
- 滚动节流优化:给滚动监听事件添加节流函数,减少高频触发,提升性能。
🎯 核心知识点总结(凝练精准,实战导向)
- 滚动 API :掌握
window.pageYOffset(获取滚动位置)、window.scrollTo(设置滚动位置); - 动画实现 :使用
requestAnimationFrame实现高性能帧动画,替代传统定时器; - DOM 位置计算 :
getBoundingClientRect()/offsetTop/offsetHeight,精准获取元素位置; - 自定义属性 :
data-target实现元素间关联,解耦 HTML 结构和 JS 逻辑; - 事件监听:同时监听点击和滚动事件,实现交互闭环;
- 性能优化 :理解
requestAnimationFrame相比setInterval的优势,提升动画流畅度。
📢 互动留言
跟着案例实操一遍,轻松实现页面平滑滚动效果!这个功能在官网、长页面、文档类网站中高频使用,复制代码就能直接复用。如果遇到滚动不精准、动画卡顿等问题,评论区留言,我会第一时间解答;你还想给滚动功能添加哪些扩展(比如回到顶部、滚动进度条),也可以留言告诉我~