轮播图、回到顶部、吸顶导航,这几样几乎是网页项目的标配。所谓 JS 特效,本质是用 JavaScript 监听用户行为或滚动位置,再改动 DOM 样式制造视觉反馈。高频特效大约八类,每一类都有固定套路,下面逐个盘点,并给常用特效的关键代码。

网页高频特效清单
- 轮播图:定时器切换图片索引,配合位移或透明度过渡,自动播放前加节流标记,防止动画叠加越点越快。
- 回到顶部:监听滚动,页面滚过一屏时显示按钮,点击后平滑滚回顶端。
- 吸顶导航:导航条滚过阈值后固定在顶部,长页面里帮用户随时保住操作入口。
- 图片懒加载:真实地址先存在 data-src,元素进入视口再赋值给 src,首屏请求数明显下降。
- 打字机效果:定时器逐字截取字符串写入元素,常配闪烁光标,多用于首页标语。
- 无限加载:列表尾部放一个哨兵元素,进入视口就请求下一页数据,替代翻页按钮。
- 进场动画:元素进入视口时加一个类名,触发 CSS 过渡,常见于卡片列表渐次浮现。
- 粒子背景:Canvas 上画点与连线,requestAnimationFrame 循环重绘,密集计算放进动画帧。
这八类覆盖了绝大多数业务页面的动效需求。动手前先想清楚一件事:特效是给内容服务的,一个页面挑两三处点缀足够,处处都动反而稀释了重点,也拉低了低端机上的流畅度。以最常见也最容易写砸的轮播图为例,可以按这四步搭起来:
- 外层容器定宽裁切,内层横向排列全部图片;
- 定时器每两三秒递增索引,位移用 transform 过渡;
- 首尾循环用”克隆首图”或索引取模处理,避免跳变;
- 箭头、圆点、触摸滑动各绑事件,统一走同一个切换函数。
图片懒加载的实现要点
懒加载的收益来自”按需加载”:首屏只请求看得见的图,其余等滚动到位再说。核心工具是 IntersectionObserver,它异步检测元素与视口的交叉状态,替代过去在 scroll 事件里反复调用 getBoundingClientRect 的写法,主线程压力小得多。
const io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const el = entry.target;
el.src = el.dataset.src; // 进入视口才加载真实地址
io.unobserve(el);
});
}, { rootMargin: '200px' }); // 提前 200px 预载,滚动到位时多半已加载完
document.querySelectorAll('[data-src]').forEach((el) => io.observe(el));
落地时三个细节决定体验好坏:
- 未加载前给固定宽高或占位图,避免图片加载完页面猛地跳动;
- rootMargin 给出预载余量,用户几乎感知不到加载过程;
- 加载失败要有回退,至少展示一张默认图,别留空白。
打字机与回到顶部的写法
打字机的思路是把字符串当队列,一次吐一个字符,实现只要四步:
- 准备目标文案和承载元素,初始内容置空;
- 用计数器截取字符串前 i 位写入 textContent;
- setTimeout 定时递增计数器,间隔参数控制语速;
- 计数器越过长度即停止,需要循环就加退格分支重来。
const text = '这段文字会被一个字一个字敲出来';
const box = document.querySelector('#demo');
let i = 0;
(function type() {
box.textContent = text.slice(0, i++);
if (i <= text.length) setTimeout(type, 80);
})();
只写 textContent 就好,别在循环里改宽度之类的布局属性,否则每个字都要触一次重排;配一段 CSS 闪烁光标更逼真。回到顶部则更简单,现代浏览器原生支持平滑滚动,不需要自己写缓动函数:
document.querySelector('#toTop').addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
按钮显隐同样有固定套路,三步就够:
- 监听 scroll,取当前滚动距离与一屏高度比较;
- 滚过一屏时给按钮加显示类名,滚回去再移除;
- 点击回调里调 scrollTo 平滑滚顶,全程不用自己写缓动。
阈值判断别省,避免按钮在页面顶端一闪而过。事件绑定统一用 addEventListener,别把逻辑写在 HTML 属性里。
吸顶与无限加载的两种方案
吸顶导航有两条路线,选型看浏览器兼容要求:
- CSS 方案:一条
position: sticky; top: 0搞定,性能好、代码少,现代浏览器都支持; - JS 方案:老浏览器兜底,滚动到阈值后给导航切换 fixed 类名;
- 用 JS 方案时留一个等高占位元素,否则吸顶瞬间内容会上跳一截。
无限加载和懒加载共用 IntersectionObserver 的套路,差别只在回调里做什么:懒加载换地址,无限加载去请求下一页。请求要有状态锁,上一页没回来之前不重复触发;数据加载完记得断开监听,否则到底之后还会空发请求。
特效对比速查
| 特效 | 核心 API | 实现难度 | 常见坑 |
|---|---|---|---|
| 轮播图 | setInterval + transform | 中 | 动画叠加、首尾循环跳变 |
| 回到顶部 | scrollTo | 低 | 无显示阈值导致按钮闪现 |
| 吸顶导航 | position: sticky | 低 | 父元素设 overflow 后失效 |
| 图片懒加载 | IntersectionObserver | 低 | 未加载前无占位,页面跳动 |
| 打字机 | setTimeout | 低 | 循环里改布局属性引发重排 |
| 无限加载 | IntersectionObserver | 中 | 没加状态锁导致重复请求 |
| 进场动画 | classList + CSS 过渡 | 低 | 动画了宽高等重排属性 |
| 粒子背景 | Canvas + requestAnimationFrame | 中 | 粒子数过多导致掉帧 |
进场动画的写法也值得单列一步,几乎每个列表页都用得上:
- 初始样式里把卡片设为透明并向下偏移一点;
- IntersectionObserver 监听元素进入视口;
- 进入时用 classList 加上目标类名,CSS transition 接管过渡;
- 只动画一次的场景,加完类名就 unobserve,省掉后续回调。
性能与体验的边界
特效堆得越多不一定越好,动效过多还会让用户分心。做的时候守住几条纪律:
- 滚动类回调必须节流,或直接换成 IntersectionObserver;
- 动画只改 transform 和 opacity,避开触发重排的宽高属性;
- 监听 scroll 时加 passive: true,滚动线程不被回调阻塞;
- 尊重系统的”减弱动态效果”设置,媒体查询命中时关掉非必要动画。
库还是原生写,判断标准也简单:单个简单特效,原生几十行就够,引库反增体积;轮播、粒子这类交互复杂、边界情况多的,用成熟库更稳。守着这几条,页面在低端机上也能保住帧率,常见特效的实现路径和边界就都齐了。
常见问题(FAQ)
Q1:简单特效能不能不用库直接原生写?
能。渐显、回到顶部、懒加载这类几十行内搞定,引库反增体积。
Q2:图片懒加载哪种实现方式好?
简单场景用原生 loading 属性,需要占位和失败回退时用 IntersectionObserver。
Q3:页面特效多了变卡怎么办?
先查回调里有没有触发布局,再把动画限制在 transform 和 opacity 上。