做网站久了你会发现,真正让人「哇」出声的,往往不是内容本身,而是滚动的过程。
所以这次我干脆做了个滚动视觉实验页——单文件、零依赖、纯原生 JS,双击就能打开,不用装任何东西。黑金橙配色、玻璃态卡片、直角切角按钮,一点蓝紫渐变都没有。

下面把最值钱的 5 段代码拆给你。
一、顶部滚动进度条
一行 scroll 事件 + 实时百分比,最廉价但最有效的情绪价值:
const progress = document.getElementById(‘progress’);
addEventListener(‘scroll’, () => {
const h = document.documentElement.scrollHeight – innerHeight;
progress.style.width = (scrollY / h * 100) + ‘%’;
}, { passive: true });
配合这条渐变 CSS,进度条自带发光:
#progress {
position: fixed; top: 0; left: 0; height: 3px; z-index: 99;
background: linear-gradient(90deg, #e05a00, #ff8a1e, #ffc25c);
box-shadow: 0 0 12px rgba(255,138,30,.8);
}
二、元素进视口才出场(reveal + 计数器)
别用 scroll 挨个量距离,IntersectionObserver 是正道,性能好一个量级:
const io = new IntersectionObserver(es => {
es.forEach(e => {
if (!e.isIntersecting) return;
e.target.classList.add(‘on’); // 触发 CSS 过渡
io.unobserve(e.target); // 只演一次
});
}, { threshold: .18 });
document.querySelectorAll(‘.reveal’).forEach(el => io.observe(el));
.reveal {
opacity: 0; transform: translateY(60px);
transition: .9s cubic-bezier(.16,1,.3,1);
}
.reveal.on { opacity: 1; transform: none; }
数字计数器同理,进视口后用 requestAnimationFrame 跑 1.6 秒的 easeOutCubic,数字从 0 冲到目标值。
三、sticky 钉住叙事(整页最上头的一段)
一个高 400vh 的容器,里面塞一个 position: sticky; top: 0 的满屏子元素。滚动距离被换算成 0–1 的进度,再切成 4 步:
整页就一个 HTML 文件,没有构建、没有依赖、没有 node_modules。适合产品发布页、作品集首屏、活动专题——凡是想用「滚动」从上到下讲一个故事的页面,套上内容就能用。
粒子是 Canvas,出场是 Observer,钉住是 sticky,其余全是 CSS。黑科技为零,效果不打折。

评论(0)