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

如何在滚动经过图片时将sticky侧边栏文本颜色从黑色改为白色

实现思路

核心用IntersectionObserver API检测sticky文本和图片的重叠状态,性能远高于高频监听scroll事件,适配任意垂直滚动场景。

第一步:调整基础CSS

先把侧边栏文本调整为sticky定位,同时保留原有类的样式:

/* 新增sticky侧边栏容器样式,可根据实际布局调整位置 */
.sidebar {
  position: sticky;
  top: 20px;
  left: 5%;
  z-index: 1; /* 低于图片容器的z-index:2,重叠时图片在上 */
  width: fit-content;
}
.section-title {
  margin: 0;
  font-size: 64px;
  white-space: nowrap;
  color: #000; /* 默认黑色 */
  transition: color 0.2s ease; /* 加过渡效果更自然 */
}
.section-title.on-dark {
  color: #fff; /* 重叠时切换为白色 */
}
.image-container {
  padding-bottom: 100%;
  background: black;
  position: relative;
  overflow: hidden;
  z-index: 2;
  background-size: cover;
  background-position: center;
  margin-bottom: 200px; /* 图片之间留间距,方便观察效果 */
}

第二步:添加重叠检测JS逻辑

不需要修改原有视差动画的代码,新增以下逻辑即可:

const stickyText = document.querySelector('.section-title');
const imageContainers = document.querySelectorAll('.image-container');

// 创建重叠检测器
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    // 图片和sticky文本进入重叠状态时加类,离开时移除
    entry.isIntersecting ? stickyText.classList.add('on-dark') : stickyText.classList.remove('on-dark');
  });
}, {
  root: null, // 以视口为检测基准
  threshold: 0, // 只要有1px重叠就触发
  rootMargin: "0px 0px 0px -10%" /* 调整该值控制触发时机,负值表示从左侧内缩检测范围,匹配侧边栏位置 */
});

// 给所有图片容器绑定监听
imageContainers.forEach(img => observer.observe(img));

完整可运行代码

可直接复制测试效果:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>滚动文本变色示例</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      min-height: 3000px;
      font-family: 'Montserrat', sans-serif;
    }
    .sidebar {
      position: sticky;
      top: 20px;
      left: 5%;
      z-index: 1;
      width: fit-content;
    }
    .outer-container {
      max-width: 600px;
      margin: 0 25% 0 auto;
      width: 60%;
      padding: 200px 0px;
      position: relative;
    }
    .image-container {
      padding-bottom: 100%;
      position: relative;
      overflow: hidden;
      z-index: 2;
      background-size: cover;
      background-position: center;
      margin-bottom: 200px;
      background-image: url('https://images.unsplash.com/photo-1570528812862-9984124e7e22?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ');
    }
    .section-title {
      font-size: 64px;
      white-space: nowrap;
      color: #000;
      transition: color 0.2s ease;
    }
    .section-title.on-dark {
      color: #fff;
    }
    .section-title span {
      position: relative;
      display: block;
    }
  </style>
</head>
<body>
  <!-- 侧边栏sticky文本 -->
  <div class="sidebar">
    <h2 class="section-title">
      <span class="paralax-title">
        Live The Adventure
      </span>
    </h2>
  </div>

  <!-- 内容区图片 -->
  <div class="outer-container">
    <div class="image-container"></div>
    <div class="image-container" style="background-image: url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb')"></div>
    <div class="image-container" style="background-image: url('https://images.unsplash.com/photo-1551632811-561732d1e306?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb')"></div>
  </div>

  <script>
    // 原有视差动画代码,不需要可直接删除
    let didScroll = false;
    const paralaxTitles = document.querySelectorAll('.paralax-title');
    const scrollInProgress = () => {
      didScroll = true
    }
    const raf = () => {
      if(didScroll) {
        paralaxTitles.forEach((element, index) => {
          element.style.transform = "translateX("+ window.scrollY / 10 + "%)"
        })
        didScroll = false;
      }
      requestAnimationFrame(raf);
    }
    requestAnimationFrame(raf);
    window.addEventListener('scroll', scrollInProgress)

    // 重叠检测逻辑
    const stickyText = document.querySelector('.section-title');
    const imageContainers = document.querySelectorAll('.image-container');
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        entry.isIntersecting ? stickyText.classList.add('on-dark') : stickyText.classList.remove('on-dark');
      });
    }, {
      root: null,
      threshold: 0,
      rootMargin: "0px 0px 0px -10%"
    });
    imageContainers.forEach(img => observer.observe(img));
  </script>
</body>
</html>

参数调整说明

  • 不需要横向视差效果的话,直接删除对应的JS代码和.paralax-title相关样式即可
  • 修改rootMargin的最后一个值可以调整触发变色的时机,值越小(负的越多),需要文本进入图片区域更多才会触发变色
  • 侧边栏在右侧的话,把rootMargin的最后一个值改成正的即可适配。

内容的提问来源于stack exchange,提问作者Josef Lundström

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 16:45:02