I have 3 blocks, the second block has a line and a rolling circle. The circles will scroll with the main scroll, and when scrolling stops, the circles will stick to the nearest point, which is the center of each block
But I have a problem here where when the main scroll crosses the second block, the circles stop scrolling and behave incorrectly on the page
Is it possible to complete the script so that when the main scroll passes through block2
, the circle will automatically stick to the last case and stop scrolling completely? When we go back and cross block2
accordingly, it should work again
Generally speaking, the problem is with the last case, when the scroll reaches it, the circle does not move further, maybe there is another solution instead of the option I suggested
I need the scrolling from the start to the end of block2
to be smooth and when the scrolling stops, the circle should stick to the center of the nearest case
In my example this is what happens now, if you stop scrolling then it stays where it needs to be, but for me it doesn't scroll properly to the end
const circle = document.querySelector(".circle"); const cases = document.querySelectorAll(".case"); let timer = null; const detectCase = () => { const circleCenter = circle.offsetTop + circle.offsetHeight / 2; let activeCase = null, minDist = Infinity; cases.forEach((elem) => { const caseCenter = elem.offsetTop + elem.offsetHeight / 2; const dist = Math.abs(caseCenter - circleCenter); if (dist < minDist) { minDist = dist; activeCase = elem; } }); return activeCase; }; const handleScroll = () => { const { height: blockHeight } = document.querySelector(".block2").getBoundingClientRect(); const maxTop = cases[cases.length - 1].offsetTop; const minTop = cases[0].offsetTop; let { height: startTop } = cases[0].getBoundingClientRect(); let scrollDist = startTop / 2 + window.scrollY; scrollDist = scrollDist > maxTop ? maxTop : scrollDist < minTop ? minTop : scrollDist; circle.style.top = `${scrollDist}px`; circle.style.backgroundSize = `15px ${blockHeight}px`; circle.style.backgroundPosition = `0 ${-scrollDist}px`; if (timer) return; timer = setTimeout(() => { const active = detectCase(); const activePos = active.offsetTop + active.offsetHeight / 2; circle.style.top = `${activePos}px`; circle.style.backgroundPosition = `0 ${-activePos}px`; circle.style.transition = "0.5s"; timer = null; }, 800); circle.style.transition = ""; }; const handleWindowSize = () => { if (window.innerWidth >= 991) { window.addEventListener("scroll", handleScroll); window.addEventListener("resize", handleScroll); } else { window.removeEventListener("scroll", handleScroll); window.removeEventListener("resize", handleScroll); } }; handleScroll(); handleWindowSize(); window.addEventListener("resize", handleWindowSize);
.block1 { height: 200px; background-color: gray; } .block3 { height: 600px; background-color: gray; } .block2 { height: 100%; position: relative; } .block2, .block2 .circle { background: linear-gradient(214deg, rgba(79, 142, 255, 0) 0%, #4f8eff 10%, #f5e550 90%, rgba(79, 142, 255, 0) 100%) center/3px calc(100% - 100px) no-repeat; } .block2 .circle { width: 15px; height: 15px; left: calc(50% - 8px); } .block2 .circle, .block2 .circle::before { position: absolute; border-radius: 50%; } .block2 .circle::before { content: ""; inset: 3px; background-color: white; } .text { text-align: center; padding: 200px 50px; }
<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"></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>
If I understand your question correctly, I think you can add the half-height of the last case to the
maxTop
calculation like this:This way, the maximum top position of the circle will be in the middle of the last case. Please check the updated code snippet: