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

滚动动画:固定与绝对定位切换的实现问题求助

解决方案

以下是实现你需求的完整代码示例,核心是通过滚动方向判断和状态标记解决上下滚动的逻辑冲突,同时精准控制各区块的定位与动画触发时机:

HTML 结构

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>区块滚动动画</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <!-- 前置占位内容,提供滚动空间 -->
    <div class="placeholder pre">
        <p>向下滚动查看效果</p>
        <p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p>
        <p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p>
    </div>

    <div class="bloc" id="block4">区块4</div>
    <div class="bloc" id="block5">区块5</div>
    <div class="bloc" id="block6">区块6</div>

    <!-- 后置占位内容,提供滚动空间 -->
    <div class="placeholder post">
        <p>继续滚动...</p>
        <p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p>
        <p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p>
        <p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p><p>占位内容</p>
    </div>

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

CSS 样式

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

.placeholder {
    min-height: 80vh;
    padding: 2rem;
    background: #f5f5f5;
}

.bloc {
    width: 100%;
    height: 200px;
    line-height: 200px;
    text-align: center;
    font-size: 2rem;
    color: #fff;
    transition: all 0.5s ease;
    position: relative;
}

#block4 { background: #2d3436; }
#block5 { background: #0984e3; }
#block6 { background: #e17055; }

/* 初始隐藏区块5、6 */
#block5, #block6 {
    opacity: 0;
    transform: translateY(100%);
}

/* 固定定位类 */
.fixed-top {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
}

.fixed-block5 {
    position: fixed;
    top: 400px; /* 区块4高度200px + 间距200px */
    left: 0;
    right: 0;
    opacity: 1;
    transform: translateY(0);
}

.fixed-block6 {
    position: fixed;
    top: 600px; /* 区块4+5高度400px + 间距200px */
    left: 0;
    right: 0;
    opacity: 1;
    transform: translateY(0);
}

/* 区块联动移动状态 */
.group-move {
    position: absolute;
    top: auto;
}

/* 向下淡出动画类 */
.slide-down-fade {
    opacity: 0;
    transform: translateY(100%);
}

JavaScript 逻辑

const block4 = document.getElementById('block4');
const block5 = document.getElementById('block5');
const block6 = document.getElementById('block6');

// 计算关键滚动触发点
const block4Top = block4.offsetTop;
const blockHeight = block4.offsetHeight;
const spacing = 200; // 区块间距
const triggerBlock5 = block4Top + blockHeight + spacing;
const triggerBlock6 = triggerBlock5 + blockHeight + spacing;
const triggerGroupMove = block6.offsetTop + blockHeight;

// 状态控制变量
let lastScrollTop = 0;
let isGroupMoving = false;

window.addEventListener('scroll', () => {
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    const isScrollDown = scrollTop > lastScrollTop;

    // 向下滚动逻辑
    if (isScrollDown) {
        // 重置向上滚动的动画类
        block5.classList.remove('slide-down-fade');
        block6.classList.remove('slide-down-fade');

        // 步骤1:区块4固定到顶部
        if (scrollTop >= block4Top && !isGroupMoving) {
            block4.classList.add('fixed-top');
        }

        // 步骤2:区块5从下方滑入并固定
        if (scrollTop >= triggerBlock5 && !isGroupMoving) {
            block5.classList.add('fixed-block5');
        }

        // 步骤3:区块6从下方滑入并固定
        if (scrollTop >= triggerBlock6 && !isGroupMoving) {
            block6.classList.add('fixed-block6');
        }

        // 步骤4:三个区块联动向上移动
        if (scrollTop + window.innerHeight >= triggerGroupMove) {
            isGroupMoving = true;
            // 取消固定定位,切换为绝对定位联动
            block4.classList.remove('fixed-top');
            block5.classList.remove('fixed-block5');
            block6.classList.remove('fixed-block6');
            block4.classList.add('group-move');
            block5.classList.add('group-move');
            block6.classList.add('group-move');
            // 保持间距同步移动
            const offset = scrollTop - block4Top;
            block4.style.top = `${offset}px`;
            block5.style.top = `${offset + blockHeight + spacing}px`;
            block6.style.top = `${offset + (blockHeight + spacing) * 2}px`;
        }
    } 
    // 向上滚动逻辑
    else {
        // 从联动状态返回固定状态
        if (isGroupMoving) {
            const offset = scrollTop - block4Top;
            block4.style.top = `${offset}px`;
            block5.style.top = `${offset + blockHeight + spacing}px`;
            block6.style.top = `${offset + (blockHeight + spacing) * 2}px`;

            // 回到区块6固定触发点,恢复固定状态
            if (scrollTop <= triggerBlock6) {
                isGroupMoving = false;
                block4.classList.remove('group-move');
                block5.classList.remove('group-move');
                block6.classList.remove('group-move');
                block4.style.top = '';
                block5.style.top = '';
                block6.style.top = '';
                block4.classList.add('fixed-top');
                block5.classList.add('fixed-block5');
                block6.classList.add('fixed-block6');
            }
        } 
        // 回到区块4初始位置,依次淡出区块6、5
        else if (scrollTop <= block4Top) {
            block4.classList.remove('fixed-top');
            
            // 步骤5:区块6向下淡出
            if (scrollTop <= triggerBlock6 - (blockHeight + spacing) * 2) {
                block6.classList.add('slide-down-fade');
                block6.classList.remove('fixed-block6');
            }

            // 步骤6:区块5向下淡出
            if (scrollTop <= triggerBlock5 - (blockHeight + spacing)) {
                block5.classList.add('slide-down-fade');
                block5.classList.remove('fixed-block5');
            }
        }
    }

    lastScrollTop = scrollTop;
});

// 页面加载初始化隐藏区块5、6
window.addEventListener('load', () => {
    block5.style.opacity = '0';
    block5.style.transform = 'translateY(100%)';
    block6.style.opacity = '0';
    block6.style.transform = 'translateY(100%)';
});

关键解决点说明

  1. 滚动方向判断:通过对比当前与上一次滚动位置,明确区分上下滚动逻辑,避免状态冲突;
  2. 状态标记:用isGroupMoving标记是否处于区块联动状态,防止不同阶段的定位逻辑互相干扰;
  3. 精准触发点:提前计算各动画的滚动触发位置,确保动画时机准确;
  4. 分层逻辑处理:向上滚动时先处理从联动状态返回的情况,再处理区块淡出逻辑,保证步骤5、6的顺序执行。

内容的提问来源于stack exchange,提问作者Diamondra Sandra RAVONISON

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 18:23:14