基于页面滚动实现圆形标记动态滑动与吸附的方案咨询
问题描述
我在页面上设置了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
相关产品推荐
相关产品推荐

