【JS 实战案例】用 JS 实现页面滚动到指定位置(带动画)

🎯 案例核心效果

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

📌 实现思路(极简清晰)

分 3 步落地,逻辑简单易上手,零基础也能快速掌握:

  1. HTML :搭建导航栏和锚点区块,通过id和自定义属性绑定对应关系;
  2. CSS:美化导航栏和区块样式,突出选中状态,保证页面布局美观;
  3. 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变量可自定义滚动速度,适配不同场景需求。

💡 新手常见踩坑点(精准避坑,少走弯路)

  1. 滚动后区块被导航遮挡? → 计算目标位置时未减去导航栏高度,需在targetPosition中减去导航栏高度(示例中因导航栏高度已通过margin-top抵消,故设为 0);
  2. 滚动动画卡顿? → 使用setInterval代替requestAnimationFrame,后者是浏览器原生动画 API,更适配屏幕刷新率;
  3. 选中状态不同步? → 滚动监听时判断条件不合理,需以可视区域中间位置(window.innerHeight / 2)为基准,而非顶部;
  4. 移动端滚动异常? → 未考虑视口高度变化,区块使用100vh而非固定高度,适配不同屏幕;
  5. 滚动到目标位置不精准? → 步长计算未做边界判断,需在scrollAnimation中判断Math.abs(step) < 1时直接定位到目标位置。

🔧 扩展思路(落地性强,新手可练)

  1. 滚动速度适配:根据滚动距离动态调整速度,长距离滚动更快,短距离更慢;
  2. 滚动禁用:添加开关,滚动过程中禁用导航按钮点击,避免重复触发;
  3. 回到顶部按钮:添加回到页面顶部的按钮,复用平滑滚动函数;
  4. 滚动进度条:添加页面滚动进度条,实时显示当前滚动位置占总长度的比例;
  5. 滚动节流优化:给滚动监听事件添加节流函数,减少高频触发,提升性能。

🎯 核心知识点总结(凝练精准,实战导向)

  1. 滚动 API :掌握window.pageYOffset(获取滚动位置)、window.scrollTo(设置滚动位置);
  2. 动画实现 :使用requestAnimationFrame实现高性能帧动画,替代传统定时器;
  3. DOM 位置计算getBoundingClientRect()/offsetTop/offsetHeight,精准获取元素位置;
  4. 自定义属性data-target实现元素间关联,解耦 HTML 结构和 JS 逻辑;
  5. 事件监听:同时监听点击和滚动事件,实现交互闭环;
  6. 性能优化 :理解requestAnimationFrame相比setInterval的优势,提升动画流畅度。

📢 互动留言

跟着案例实操一遍,轻松实现页面平滑滚动效果!这个功能在官网、长页面、文档类网站中高频使用,复制代码就能直接复用。如果遇到滚动不精准、动画卡顿等问题,评论区留言,我会第一时间解答;你还想给滚动功能添加哪些扩展(比如回到顶部、滚动进度条),也可以留言告诉我~

相关推荐
We་ct2 小时前
React 更新触发原理详解
开发语言·前端·javascript·react.js·面试·前端框架·react
还是大剑师兰特2 小时前
Vue3 页面权限控制实战示例(路由守卫 + 权限判断)
开发语言·前端·javascript
跟着珅聪学java2 小时前
Vue 2 + CommonJS 写法开发教程
前端·javascript·vue.js
qq_246100053 小时前
CSDN risk probe 1773588273
开发语言·javascript·ecmascript
ByteCraze3 小时前
Vue 递归组件实战:手写一个文件/文件夹树形组件
javascript·vue.js·ecmascript
前端Hardy3 小时前
前端如何防止用户重复提交表单?4 种可靠方案(附防坑指南)
前端·javascript·面试
前端Hardy3 小时前
用户真的关掉页面了吗?前端精准检测页面卸载的 4 种方法(附避坑指南)
前端·javascript·面试
yangyanping201083 小时前
Vue入门到精通七之关键字const
前端·javascript·vue.js