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

基于页面滚动实现圆形标记动态滑动与吸附的方案咨询

问题描述

我在页面上设置了3个区块,第二个区块内包含3个(或更多)case,区块带有背景渐变线条,当前仅第一个case对应的<div class="col-2">容器内有一个圆形标记。现有代码如下:

CSS代码

.block1 {
  height: 200px;
  background-color: gray;
}

.block2 {
  height: 100%;
  background: linear-gradient(214deg, rgba(79, 142, 255, 0.00) 0%, #4F8EFF 10%, #F5E550 90%, rgba(79, 142, 255, 0.00) 100%) center / 3px calc(100% - 100px) no-repeat;
  
  .circle {
    position: relative;
    width: 15px;
    height: 15px;
    background-color: white;
    border: solid 3px #4F8EFF;
    border-radius: 50%;
    top: 50%;
    left: calc(50% - 8px);
  }
}

.text {
  text-align: center;
  padding: 200px 50px;
}

.block3 {
  height: 200px;
  background-color: gray;
}

HTML代码

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" integrity="sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>

<div class="block1"></div>

<div class="block2">
  <div class="case">
    <div class="row">
      <div class="col-5 text">Text 1</div>
      <div class="col-2">
        <div class="circle"></div>
       </div>
      <div class="col-5 text">Text 1</div>
    </div>
  </div>
  <div class="case">
    <div class="row">
      <div class="col-5 text">Text 2</div>
      <div class="col-2"></div>
      <div class="col-5 text">Text 2</div>
    </div>
  </div>
  <div class="case">
    <div class="row">
      <div class="col-5 text">Text 3</div>
      <div class="col-2"></div>
      <div class="col-5 text">Text 3</div>
    </div>
  </div>
</div>

<div class="block3"></div>

需求:能否实现根据页面滚动状态,让圆形标记沿线条在所有case的<div class="col-2">容器间移动——滚动时跟随滑动,滚动停止时自动定位到最近的case;且圆形标记的颜色需与线条颜色保持一致?具体来说,当页面处于第一个case的可视区域中间时,圆形标记位于第一个case处;滚动到第二个case区域时,圆形标记随之移动到对应位置。


实现方案

通过调整DOM结构、更新CSS样式并添加滚动监听逻辑,可完全实现需求效果:

1. 调整HTML结构

将圆形标记从单个col-2中移出,改为独立元素放在.block2内部,方便全局控制位置:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" integrity="sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>

<div class="block1"></div>

<div class="block2">
  <!-- 独立的圆形标记 -->
  <div class="circle"></div>
  
  <div class="case">
    <div class="row">
      <div class="col-5 text">Text 1</div>
      <div class="col-2 case-marker"></div>
      <div class="col-5 text">Text 1</div>
    </div>
  </div>
  <div class="case">
    <div class="row">
      <div class="col-5 text">Text 2</div>
      <div class="col-2 case-marker"></div>
      <div class="col-5 text">Text 2</div>
    </div>
  </div>
  <div class="case">
    <div class="row">
      <div class="col-5 text">Text 3</div>
      <div class="col-2 case-marker"></div>
      <div class="col-5 text">Text 3</div>
    </div>
  </div>
</div>

<div class="block3"></div>

2. 更新CSS样式

  • 给.block2添加相对定位,作为圆形标记的定位参考
  • 将.circle改为绝对定位,实现自由移动
  • 让圆形标记的边框渐变与背景线条保持一致:
.block1 {
  height: 200px;
  background-color: gray;
}

.block2 {
  position: relative; /* 作为绝对定位的父容器 */
  height: 100%;
  background: linear-gradient(214deg, rgba(79, 142, 255, 0.00) 0%, #4F8EFF 10%, #F5E550 90%, rgba(79, 142, 255, 0.00) 100%) center / 3px calc(100% - 100px) no-repeat;
}

.circle {
  position: absolute;
  width: 15px;
  height: 15px;
  background-color: white;
  border-radius: 50%;
  left: calc(50% - 9px); /* 居中对齐背景线条 */
  top: 0;
  /* 边框渐变与背景线条一致 */
  border: solid 3px transparent;
  background-clip: padding-box, border-box;
  background-origin: padding-box, border-box;
  background-image: linear-gradient(white, white), linear-gradient(214deg, rgba(79, 142, 255, 0.00) 0%, #4F8EFF 10%, #F5E550 90%, rgba(79, 142, 255, 0.00) 100%);
  transition: top 0.3s ease; /* 滚动停止后的平滑过渡 */
}

.text {
  text-align: center;
  padding: 200px 50px;
}

.block3 {
  height: 200px;
  background-color: gray;
}

3. 添加JavaScript逻辑

监听页面滚动事件,实时计算圆形标记位置;滚动停止后自动吸附到最近的case:

const block2 = document.querySelector('.block2');
const circle = document.querySelector('.circle');
const caseMarkers = document.querySelectorAll('.case-marker');
let scrollTimeout;

// 预计算每个case标记的垂直位置(相对于block2)
const casePositions = Array.from(caseMarkers).map(marker => {
  const rect = marker.getBoundingClientRect();
  const blockRect = block2.getBoundingClientRect();
  return rect.top - blockRect.top + marker.offsetHeight / 2 - circle.offsetHeight / 2;
});

// 滚动事件处理函数
function handleScroll() {
  clearTimeout(scrollTimeout);
  
  const blockRect = block2.getBoundingClientRect();
  // 计算当前滚动位置对应的圆形标记top值
  const scrollTop = window.scrollY;
  const circleTop = scrollTop - blockRect.top + window.innerHeight / 2 - circle.offsetHeight / 2;
  
  // 限制圆形标记在背景线条的有效范围内
  const minTop = 50; // 对应背景线条顶部偏移
  const maxTop = blockRect.height - 50 - circle.offsetHeight; // 对应背景线条底部偏移
  const clampedTop = Math.max(minTop, Math.min(maxTop, circleTop));
  
  circle.style.top = `${clampedTop}px`;
  
  // 滚动停止后自动定位到最近的case
  scrollTimeout = setTimeout(() => {
    const currentTop = parseFloat(circle.style.top);
    // 找到距离当前位置最近的case索引
    const closestIndex = casePositions.reduce((closestIdx, pos, idx) => {
      const currentDiff = Math.abs(pos - currentTop);
      const closestDiff = Math.abs(casePositions[closestIdx] - currentTop);
      return currentDiff < closestDiff ? idx : closestIdx;
    }, 0);
    
    circle.style.top = `${casePositions[closestIndex]}px`;
  }, 200);
}

// 初始化圆形标记到第一个case位置
circle.style.top = `${casePositions[0]}px`;

// 添加滚动监听
window.addEventListener('scroll', handleScroll);

效果说明

  • 滚动页面时,圆形标记会跟随滚动位置沿垂直中线移动,始终对应可视区域中间的case位置
  • 滚动停止200ms后,圆形标记会平滑过渡到最近的case位置
  • 圆形标记的边框渐变与背景线条完全一致,视觉统一

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:27:53