0

0

js如何实现页面平滑滚动

小老鼠

小老鼠

发布时间:2025-08-20 13:10:02

|

916人浏览过

|

来源于php中文网

原创

实现页面平滑滚动主要有两种方式:一是使用javascript的scrollintoview({ behavior: 'smooth' })方法,简单高效,适用于大多数现代浏览器;二是结合requestanimationframe与window.scrollto()手动实现,可自定义滚动速度、缓动曲线及回调函数,适合需要精细控制的场景。2. css的scroll-behavior: smooth属性能全局启用平滑滚动,代码简洁且性能好,但缺乏对滚动过程的控制能力,无法处理复杂逻辑如偏移调整或执行回调,而javascript方案则具备更高的灵活性和兼容性。3. 性能优化应优先使用requestanimationframe而非settimeout,避免动画卡顿,并减少滚动循环中的dom操作,同时对scroll事件使用节流或防抖以提升整体性能。4. 兼容性方面,scrollintoview和css scroll-behavior在旧版浏览器(如ie)中支持较差,需提供降级方案,如直接跳转或使用polyfill,而requestanimationframe手动实现兼容性更广,可通过polyfill支持更老的环境。5. 平滑滚动在实际开发中可解决多种复杂问题:如固定头部遮挡内容时通过计算偏移量精准定位;动态加载内容后延迟滚动至新元素;表单验证失败时自动滚动并聚焦首个错误字段;实现“回到顶部”按钮的流畅体验;以及提升键盘导航的可访问性,帮助用户清晰感知焦点移动路径。这些应用显著增强了交互的流畅性与用户体验。

js如何实现页面平滑滚动

页面平滑滚动,说白了,就是让用户在点击某个链接或者按钮后,页面不是“唰”一下跳到目标位置,而是像坐电梯一样,缓缓地、优雅地滑过去。实现这个效果,JavaScript 提供了一些相当实用的方法,其中最直接的就是

scrollIntoView()
,如果需要更精细的控制,那
requestAnimationFrame
配合
window.scrollTo()
就能大显身手了。

解决方案

要让页面实现平滑滚动,我们通常会考虑两种主要方式:

第一种,也是我个人觉得在大多数现代浏览器环境下最省心的办法,就是利用 DOM 元素的

scrollIntoView()
方法。这个方法有一个
behavior
参数,当你把它设为
'smooth'
的时候,浏览器就会自动处理平滑滚动的动画。比如,你有一个 ID 为
targetSection
的元素,想让页面平滑滚动到它那里,你只需要这样写:

document.getElementById('targetSection').scrollIntoView({
    behavior: 'smooth',
    block: 'start' // 滚动到元素的顶部与视口顶部对齐
    // inline: 'nearest' // 如果是横向滚动,确保元素在视口内
});

这方法简直是为“懒人”设计的,它把复杂的动画逻辑都封装在浏览器内部了,我们不用操心计算时间、速度曲线这些事。但正因为如此,它的缺点也很明显:你没法自定义滚动的速度、动画曲线(easing function),或者在滚动过程中做一些额外的事情。

所以,当我们需要更高级的控制时,比如要实现自定义的缓动效果,或者在滚动结束后执行某个回调函数,那我们就得自己动手,用

requestAnimationFrame
来实现一个动画循环。这听起来可能有点复杂,但其实核心思想很简单:在每一帧动画中,我们都计算出一个新的滚动位置,然后更新
window.scrollTo()

一个简化的手动平滑滚动函数大概是这样的:

function smoothScrollTo(targetY, duration) {
    const startY = window.scrollY || window.pageYOffset;
    const distance = targetY - startY;
    let startTime = null;

    // 缓动函数,这里用一个简单的 easeInOutQuad
    const easeInOutQuad = (t, b, c, d) => {
        t /= d / 2;
        if (t < 1) return c / 2 * t * t + b;
        t--;
        return -c / 2 * (t * (t - 2) - 1) + b;
    };

    function animation(currentTime) {
        if (startTime === null) startTime = currentTime;
        const timeElapsed = currentTime - startTime;
        const run = easeInOutQuad(timeElapsed, startY, distance, duration);
        window.scrollTo(0, run);
        if (timeElapsed < duration) {
            requestAnimationFrame(animation);
        } else {
            // 确保最终位置准确,避免浮点数误差
            window.scrollTo(0, targetY);
            // 这里可以添加滚动完成后的回调
            console.log('滚动完成!');
        }
    }

    requestAnimationFrame(animation);
}

// 使用示例:滚动到页面顶部,持续800毫秒
// smoothScrollTo(0, 800);

// 滚动到某个元素,需要先获取其offsetTop
// const targetElement = document.getElementById('someElement');
// if (targetElement) {
//     smoothScrollTo(targetElement.offsetTop, 1000);
// }

这个手动实现的方式,虽然代码量大一点,但它给了你完全的自由度,可以玩转各种缓动效果,甚至可以加入中断滚动、暂停等逻辑。

为什么不直接用CSS的
scroll-behavior: smooth
?它和JS有什么区别

这个问题问得好,因为这确实是很多人会有的疑惑。CSS 的

scroll-behavior: smooth
是一个非常简洁的解决方案,你只需要在 CSS 里给
html
或某个可滚动容器加上
scroll-behavior: smooth;
,那么当用户点击一个锚点链接(
#id
)时,或者通过
window.scrollTo()
scrollIntoView()
等 JS 方法触发滚动时,浏览器就会自动以平滑的方式完成。

html {
    scroll-behavior: smooth;
}

它和 JavaScript 实现的区别,我觉得主要体现在“控制力”和“场景适应性”上。

CSS 的

scroll-behavior: smooth
是一种声明式(declarative)的控制。你告诉浏览器“我希望所有滚动都是平滑的”,然后浏览器就照办了。它的优点是:

  • 极简: 一行 CSS 代码搞定,不需要任何 JavaScript。
  • 全局性: 对所有触发滚动的行为都有效,包括浏览器自带的锚点跳转。
  • 性能: 浏览器原生实现,通常性能最优,动画流畅。

然而,它的缺点也显而易见:

  • 缺乏细粒度控制: 你无法调整滚动的速度、缓动曲线,也无法在滚动过程中或滚动结束后执行特定的 JavaScript 逻辑。它就是“平滑”,仅此而已。
  • 兼容性: 虽然现在主流浏览器支持度已经很好了,但一些旧版浏览器可能不支持。
  • 特定场景受限: 比如,你想实现一个“滚动到某个元素,但要预留50像素的顶部间距”的功能,或者“滚动到某个元素,如果它已经被固定头部遮挡了,需要额外偏移”,CSS 就不行了。

JavaScript 实现(无论是

scrollIntoView({ behavior: 'smooth' })
还是手动
requestAnimationFrame
)则是一种命令式(imperative)的控制。你一步步告诉浏览器“现在从这里滚到那里,用这个速度,这个曲线,滚完之后再干什么”。它的优势在于:

  • 高度可定制: 可以自定义滚动速度、缓动函数(例如,先快后慢,或者弹性效果)。
  • 事件监听与回调: 可以在滚动开始、进行中、结束时触发自定义逻辑。
  • 复杂逻辑处理: 能够处理固定导航栏偏移、动态内容加载后的滚动、滚动到视口外但需精确对齐的元素等复杂情况。
  • 更好的兼容性: 手动
    requestAnimationFrame
    的方式几乎可以在所有支持 JS 的浏览器上工作,你可以自己编写降级方案。

所以,我的看法是:如果你只是需要一个简单的、全局的平滑滚动效果,且不关心动画细节,那么

scroll-behavior: smooth
是首选,它最优雅。但如果你对滚动行为有更高的要求,需要自定义动画、处理复杂偏移、或者在滚动过程中有交互,那么 JavaScript 实现就是你的不二之选。
scrollIntoView({ behavior: 'smooth' })
是一个很好的折中方案,它在提供一定平滑效果的同时,也保持了简洁性,但如果你需要更深度的控制,就得考虑
requestAnimationFrame
了。

实现平滑滚动时,如何处理性能优化和兼容性问题?

在实现平滑滚动,特别是手动通过

requestAnimationFrame
来做的时候,性能和兼容性确实是需要好好琢磨的两个点。毕竟,我们不希望用户看到卡顿的动画,也不希望在某些浏览器上直接失效。

返回顶部插件jquery.gototop
返回顶部插件jquery.gototop

返回顶部插件jquery.gototop是一款当在用户向下滚动页面一段距离之后,会以CSS3动画方式出现返回顶部按钮。点击返回顶部按钮之后,页面以平滑的方式滚动回顶部。

下载

性能优化:

我觉得,最核心的性能优化点就是

requestAnimationFrame
。你可能会想,为什么不用
setTimeout
setInterval
?原因很简单:
requestAnimationFrame
会告诉浏览器“我想要在下一次重绘之前执行这个函数”。这意味着你的动画代码会在浏览器最合适的时候执行,通常是每秒60帧(如果显示器刷新率允许),而且它会在浏览器空闲时执行,这样就能避免不必要的重绘和回流,大大减少卡顿的发生。相比之下,
setTimeout
只是在一个固定时间后执行,它不会考虑浏览器当前的状态,可能在浏览器正忙的时候执行,导致动画不流畅。

另外,在动画循环内部,要尽量避免进行复杂的 DOM 操作或大量的计算。每次循环只做必要的事情:计算新的滚动位置,然后更新

window.scrollTo()
。如果你的滚动目标元素会动态变化位置(比如图片加载后),你可能需要在滚动开始前计算好最终目标位置,而不是在每一帧都重新计算。

还有一点,虽然不直接关乎滚动动画本身,但如果你在页面滚动时还监听了

scroll
事件,并且在事件处理函数中执行了比较耗时的操作,那么务必考虑使用节流(throttle)防抖(debounce)。这能有效控制事件触发频率,避免不必要的计算,从而提升整体页面性能。

兼容性问题:

兼容性方面,不同的实现方式有不同的考量。

  • scrollIntoView({ behavior: 'smooth' })
    这个方法在现代浏览器中的支持度已经非常好了,包括 Chrome、Firefox、Safari、Edge 等。但如果你需要支持 Internet Explorer 或者一些非常旧的浏览器版本,它就不行了。对于这种情况,你可以考虑一个降级方案:如果浏览器不支持
    behavior: 'smooth'
    ,就让它直接跳过去(
    element.scrollIntoView()
    ),或者使用一个 polyfill 来模拟平滑效果,但通常直接跳过去是更简单的用户体验。
  • CSS
    scroll-behavior: smooth
    同样,它在现代浏览器中表现良好,但旧版浏览器不支持。对于不支持的浏览器,页面会直接跳到目标位置,这通常是可以接受的降级。
  • 手动
    requestAnimationFrame
    实现:
    这种方式的兼容性是最好的,因为它完全依赖于 JavaScript 的基本能力。只要浏览器支持
    requestAnimationFrame
    (现代浏览器都支持),并且支持
    window.scrollTo()
    element.scrollTop
    属性,它就能工作。对于那些连
    requestAnimationFrame
    都不支持的极少数老旧浏览器(比如 IE9 及以下),你可能需要一个
    requestAnimationFrame
    的 polyfill,或者干脆降级到
    setTimeout
    ,但那样性能可能会受影响。

我的建议是,优先使用

scrollIntoView({ behavior: 'smooth' })
,因为它最简单且性能好。如果需要更高级的控制,再考虑手动
requestAnimationFrame
。在任何情况下,都要确保你的代码在目标浏览器环境中经过测试,并且有合理的降级方案,这样才能给用户提供稳定可靠的体验。

除了基本滚动,平滑滚动还能解决哪些实际开发中的复杂场景?

平滑滚动绝不仅仅是让页面看起来更酷炫那么简单,它在很多实际开发场景中都能发挥重要作用,解决一些看似棘手的问题,提升用户体验和界面的逻辑性。

首先想到的是固定导航栏(Fixed Header)的问题。你有没有遇到过,点击导航栏上的一个锚点链接,页面是滚动过去了,但目标内容却被固定在顶部的导航栏遮住了一部分?这体验可不怎么样。通过 JavaScript 实现的平滑滚动,我们可以轻松解决这个问题。在计算目标滚动位置时,我们不是直接用元素的

offsetTop
,而是用
offsetTop
减去固定导航栏的高度。这样,滚动结束后,目标内容就会恰好停留在导航栏下方,完美地呈现在用户眼前。

// 假设固定导航栏高度是 60px
const fixedHeaderHeight = 60;
const targetElement = document.getElementById('mySection');
if (targetElement) {
    const targetY = targetElement.offsetTop - fixedHeaderHeight;
    // 调用之前定义的 smoothScrollTo 函数
    smoothScrollTo(targetY, 800);
}

其次是动态加载内容后的滚动。想象一下,你的页面有一个“加载更多”按钮,点击后会通过 AJAX 异步加载新的内容,并且你希望页面能自动滚动到新加载内容的顶部。如果仅仅是简单地

scrollIntoView()
,可能在内容还没完全渲染出来的时候就触发了滚动,导致位置不准确。这时候,你可以结合异步操作的回调函数,在内容完全插入 DOM 并渲染完成后,再触发平滑滚动。这需要对异步操作和 DOM 渲染有比较好的理解。

再来就是表单验证后的焦点定位。在一个有大量输入框的表单中,如果用户提交后发现有错误,我们通常会把错误信息显示出来。但如果错误在页面下方,用户可能看不到。这时候,一个非常用户友好的做法是,自动平滑滚动到第一个出错的输入框,并将其聚焦。这能大大提升用户填写表单的效率和体验,减少他们的挫败感。

// 假设这是找到的第一个错误输入框
const firstErrorInput = document.querySelector('.input-error');
if (firstErrorInput) {
    smoothScrollTo(firstErrorInput.offsetTop - 20, 500); // 留点边距
    firstErrorInput.focus(); // 聚焦输入框
}

还有一种情况是“回到顶部”或“滚动到底部”按钮。虽然这看起来很简单,但加上平滑滚动后,用户会觉得操作更加自然和舒适,而不是生硬的跳转。对于长页面来说,这种体验上的提升是显而易见的。

最后,我想说的是辅助功能(Accessibility)方面。平滑滚动配合键盘导航可以提供更好的用户体验。例如,当用户使用 Tab 键在页面上导航时,如果焦点跳到了一个屏幕外的元素,平滑滚动可以帮助用户更好地理解焦点的移动路径,而不是突然“消失”又“出现”。这对于依赖键盘操作的用户来说,是非常有帮助的。

总的来说,平滑滚动不仅仅是一个视觉效果,它更是一种提升用户体验、优化交互流程的工具。通过它,我们可以让页面在面对各种复杂场景时,依然保持流畅、直观和易用。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

538

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

372

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

727

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

470

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

389

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

989

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

652

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

538

2023.09.20

ip地址修改教程大全
ip地址修改教程大全

本专题整合了ip地址修改教程大全,阅读下面的文章自行寻找合适的解决教程。

27

2025.12.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.7万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.6万人学习

CSS教程
CSS教程

共754课时 | 16.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号