搜索

首页  >  问答  >  正文

防止越过方块时滚动线滚动

我有 3 个块,第二个块有一条线和一个滚动圆。圆圈会随着主滚动一起滚动,当滚动停止时,圆圈会粘到最近的点,即每个块的中心

但是我在这里遇到这样的问题,当主滚动穿过第二个块时,圆圈停止滚动并且在页面上表现不正确

是否可以完成脚本,以便当主滚动穿过 block2 时,圆圈将自动坚持最后一个案例并完全停止滚动?当我们返回并穿过 block2 时,相应地,它应该再次工作

一般来说,问题在于最后一种情况,当滚动到达它时,圆圈不会进一步移动,也许还有另一种解决方案,而不是我建议的选项

我需要从block2开始到结束的滚动是平滑的,并且当滚动停止时,圆圈应该粘在最近的案例的中心

在我的示例中,这就是现在发生的情况,如果您停止滚动,那么它会停留在需要的位置,但对我来说它不会正确滚动到末尾

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>

P粉038856725P粉038856725287 天前409

全部回复(1)我来回复

  • P粉674876385

    P粉6748763852024-03-30 10:15:53

    如果我正确理解您的问题,我认为您可以将最后一个案例的半高添加到 maxTop 计算中,如下所示:

    let {
        height: lastCaseHeight
      } = cases[cases.length - 1].getBoundingClientRect();
      maxTop = maxTop + (lastCaseHeight / 2)

    这样,圆的最大顶部位置将位于最后一个案例的中间。请检查更新的代码片段:

    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();
      let maxTop = cases[cases.length - 1].offsetTop;
      const minTop = cases[0].offsetTop;
      let {
        height: startTop
      } = cases[0].getBoundingClientRect();
      
      let {
        height: lastCaseHeight
      } = cases[cases.length - 1].getBoundingClientRect();
      maxTop = maxTop + (lastCaseHeight / 2)
      
      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;
    }
    
    
    Text 1
    Text 1
    Text 2
    Text 2
    Text 3
    Text 3

    回复
    0
  • 取消回复