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

如何在React中实现类似David-hckh作品集的100vh区块局部滚动效果?

实现固定视口内内容滚动、区块整体滚动的效果

核心思路

这个效果是分阶段的滚动逻辑:

  1. 第一阶段:页面主滚动时,About区块固定占满100vh视口,内部超长内容仅在区块范围内滚动
  2. 第二阶段:内部内容滚动到底部后,继续滚动页面,整个About区块随页面向上移出视口

具体实现方案

1. React组件代码实现

import { useState, useEffect, useRef } from 'react';

const AboutSection = () => {
  const [contentScrolledToEnd, setContentScrolledToEnd] = useState(false);
  const contentRef = useRef(null);
  const sectionRef = useRef(null);

  // 监听内部内容滚动,判断是否到底部
  useEffect(() => {
    const checkScrollEnd = () => {
      const content = contentRef.current;
      if (!content) return;
      // 加10px容差避免滚动精度问题
      const isBottom = content.scrollTop + content.clientHeight >= content.scrollHeight - 10;
      setContentScrolledToEnd(isBottom);
    };

    const content = contentRef.current;
    content?.addEventListener('scroll', checkScrollEnd);
    return () => content?.removeEventListener('scroll', checkScrollEnd);
  }, []);

  // 监听页面滚动,动态切换区块定位方式
  useEffect(() => {
    const handlePageScroll = () => {
      if (!contentScrolledToEnd) return;
      const section = sectionRef.current;
      if (!section) return;

      const sectionInitialTop = section.offsetTop;
      // 页面滚动超过区块初始位置时,切换为相对定位让区块随页面滚动
      if (window.scrollY > sectionInitialTop) {
        section.style.position = 'relative';
        section.style.top = 'auto';
      } else {
        section.style.position = 'fixed';
        section.style.top = '0';
      }
    };

    window.addEventListener('scroll', handlePageScroll);
    return () => window.removeEventListener('scroll', handlePageScroll);
  }, [contentScrolledToEnd]);

  return (
    <>
      <section 
        ref={sectionRef}
        style={{
          position: contentScrolledToEnd ? 'relative' : 'fixed',
          top: contentScrolledToEnd ? 'auto' : '0',
          left: '0',
          width: '100%',
          height: '100vh',
          overflow: 'hidden',
        }}
      >
        <div style={{ display: 'flex', width: '100%', height: '100%' }}>
          {/* 左侧固定视觉区域(可放图片/3D场景) */}
          <div style={{ flex: 1, backgroundColor: '#f0f0f0' }}></div>
          {/* 右侧可滚动内容区 */}
          <div 
            ref={contentRef}
            style={{
              flex: 1,
              height: '100vh',
              overflowY: 'auto',
              padding: '2rem',
              boxSizing: 'border-box',
            }}
          >
            <h2>About Me</h2>
            {/* 这里放你的超长介绍内容,重复足够多让它超过视口高度 */}
            <p>这里是我的个人介绍...</p>
            <p>更多介绍内容...</p>
            {/* 重复内容省略 */}
          </div>
        </div>
      </section>
      {/* 页面后续区块 */}
      <section style={{ minHeight: '100vh', padding: '2rem', backgroundColor: '#fff' }}>
        页面其他内容区域
      </section>
    </>
  );
};

export default AboutSection;

2. 可选CSS优化(自定义滚动条)

/* 自定义右侧内容区的滚动条样式 */
.about-content::-webkit-scrollbar {
  width: 6px;
}

.about-content::-webkit-scrollbar-thumb {
  background-color: #888;
  border-radius: 3px;
}

.about-content::-webkit-scrollbar-track {
  background-color: #f1f1f1;
}

3. 关键逻辑说明

  • 初始状态:About区块用position: fixed固定在视口,高度100vh,内部内容容器开启overflowY: auto,实现内容在区块内独立滚动
  • 内容滚动监听:判断内容是否滚动到底部,切换状态标记
  • 页面滚动监听:当内容滚到底后,若页面滚动超过区块初始位置,将区块定位从fixed改为relative,此时区块会进入文档流,随页面一起滚动;滚动回顶部时恢复固定定位

你之前尝试无效的原因

设置min-height: 100vh加overflow: scroll的问题在于:

  • 没有区分「区块固定」和「区块滚动」两个阶段的定位逻辑,单纯的overflow只会让内容在区块内滚动,但区块本身始终占据文档流位置,无法实现"内容滚完后区块才随页面滚动"的效果
  • 必须通过JavaScript监听滚动状态,动态切换区块定位方式,才能实现分阶段的滚动效果

内容的提问来源于stack exchange,提问作者Miguel Pedraza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 02:32:53