You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现类似olaolu.dev的平滑滚动过渡效果(含按钮触发)

实现全屏滚动过渡效果(类似目标站点的页面切换动画)

核心思路

目标站点的效果是全屏区块间的平滑滚动过渡:滚动时当前区块滑出视口,目标区块同步滑入;点击导航按钮时触发完全一致的滚动动画。我们可以通过固定每个页面区块为视口高度,结合CSS过渡动画与JavaScript监听滚动/点击事件来实现。


步骤1:完善HTML结构

补充完整5个页面区块,给所有区块统一类名便于样式控制,同时给导航按钮绑定对应目标区块的ID:

<!doctype html>
<html>
<head>
    <link rel="stylesheet" href="/b/cs.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
    <!-- 固定侧边导航按钮 -->
    <div class="btn-container">
        <ul>
            <button class="btn active" data-target="section1"><i class="bi bi-app"></i></button>
            <button class="btn" data-target="section2"><i class="bi bi-diamond-fill"></i></button>
            <button class="btn" data-target="section3"><i class="bi bi-diamond-fill"></i></button>
            <button class="btn" data-target="section4"><i class="bi bi-diamond-fill"></i></button>
            <button class="btn" data-target="section5"><i class="bi bi-diamond-fill"></i></button>
        </ul>
    </div>

    <!-- 全屏页面区块 -->
    <section class="full-page active" id="section1">
        <div class="details">
            <div class="top">
                <h2>Faxraddin</h2>
                <div class="lists">
                    <button><i class="bi bi-list-check"></i></button>
                </div>
            </div>
            <div class="first-info">
                <div class="first-sec">
                    <h1>Frontend<br> Developer.</h1>
                    <h3 class="profession-info">I like to craft solid and scalable frontend products with great user experiences.</h3>
                </div>
                <img class="my-img" src="/b/images/Screenshot 2022-11-04 at 19.35.20.png">
            </div>
            <div class="some-info">
                <div class="a1">
                    <span>Highly skilled at progressive enhancement, design systems & UI Engineering.</span>
                    <span>Over a decade of experience building products for clients across several countries.</span>
                </div>
            </div>
        </div>
    </section>

    <section class="full-page" id="section2">
        <h2>页面2内容</h2>
    </section>

    <section class="full-page" id="section3">
        <h2>页面3内容</h2>
    </section>

    <section class="full-page" id="section4">
        <h2>页面4内容</h2>
    </section>

    <section class="full-page" id="section5">
        <h2>页面5内容</h2>
    </section>

    <script src="/b/js.js"></script>
</body>
</html>

步骤2:CSS实现布局与过渡动画

在cs.css中设置全屏区块基础样式,添加弹性过渡曲线,定义激活/滑出状态的位置:

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden; /* 隐藏全局滚动条,避免干扰区块切换 */
}

/* 全屏页面区块 */
.full-page {
    width: 100vw;
    height: 100vh;
    position: absolute;
    top: 0;
    left: 0;
    transition: transform 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); /* 弹性过渡,贴近目标站点效果 */
    transform: translateY(100%); /* 默认隐藏在视口下方 */
    padding: 2rem;
}

.full-page.active {
    transform: translateY(0); /* 激活状态显示在视口内 */
}

/* 向下滚动时,上一页滑出到视口上方 */
.full-page.slide-up {
    transform: translateY(-100%);
}

/* 向上滚动时,下一页滑出到视口下方 */
.full-page.slide-down {
    transform: translateY(100%);
}

/* 侧边导航按钮样式 */
.btn-container {
    position: fixed;
    right: 2rem;
    top: 50%;
    transform: translateY(-50%);
    z-index: 100;
}

.btn-container ul {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.btn {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: #fff;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    cursor: pointer;
    transition: background 0.3s;
}

.btn.active {
    background: #2c3e50;
    color: #fff;
}

步骤3:JavaScript处理交互逻辑

在js.js中实现滚动检测、页面切换动画,以及按钮点击触发逻辑:

const pages = document.querySelectorAll('.full-page');
const btns = document.querySelectorAll('.btn');
let currentPageIndex = 0;
let isAnimating = false; // 动画锁:防止未完成时重复触发

// 初始化:激活第一个页面
pages[currentPageIndex].classList.add('active');

// 监听滚轮滚动事件
window.addEventListener('wheel', (e) => {
    if (isAnimating) return;
    const direction = e.deltaY > 0 ? 'down' : 'up';
    switchPage(direction);
});

// 监听导航按钮点击事件
btns.forEach((btn, index) => {
    btn.addEventListener('click', () => {
        if (isAnimating || index === currentPageIndex) return;
        const direction = index > currentPageIndex ? 'down' : 'up';
        currentPageIndex = index;
        animatePageSwitch(direction);
        updateActiveBtn();
    });
});

// 页面切换核心逻辑
function switchPage(direction) {
    if (direction === 'down' && currentPageIndex < pages.length - 1) {
        currentPageIndex++;
        animatePageSwitch(direction);
        updateActiveBtn();
    } else if (direction === 'up' && currentPageIndex > 0) {
        currentPageIndex--;
        animatePageSwitch(direction);
        updateActiveBtn();
    }
}

// 执行页面切换动画
function animatePageSwitch(direction) {
    isAnimating = true;
    const prevPage = pages[currentPageIndex - (direction === 'down' ? 1 : -1)];
    const currentPage = pages[currentPageIndex];
    
    // 给上一页添加滑出类
    prevPage.classList.add(direction === 'down' ? 'slide-up' : 'slide-down');
    // 激活当前页
    currentPage.classList.add('active');
    
    // 动画结束后清理类名,释放动画锁
    setTimeout(() => {
        prevPage.classList.remove('active', 'slide-up', 'slide-down');
        isAnimating = false;
    }, 600); // 时间与CSS过渡时间保持一致
}

// 更新导航按钮激活状态
function updateActiveBtn() {
    btns.forEach(btn => btn.classList.remove('active'));
    btns[currentPageIndex].classList.add('active');
}

关键说明

  1. 动画曲线:使用cubic-bezier(0.68, -0.55, 0.265, 1.55)实现类似目标站点的弹性滑入效果,可根据需求调整参数。
  2. 动画锁:通过isAnimating变量避免滚动/点击在动画未完成时重复触发,防止页面状态混乱。
  3. 响应式适配:移动端可调整full-page的内边距和按钮尺寸,保证触控操作的便捷性。

内容的提问来源于stack exchange,提问作者Count of Monte Cristo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:45:32