【问题标题】:Scroll Snap only when scrolling down仅在向下滚动时滚动捕捉
【发布时间】:2022-04-05 11:49:41
【问题描述】:

我正在为 CSS 中的滚动捕捉而苦苦挣扎。

有没有办法只在一个垂直方向上使用滚动捕捉?

我想让它在向下滚动时捕捉到每个部分,但在再次向上滚动时不捕捉。 是否可以在 CSS 中使用,还是我需要一些 JavaScript?

html {
    scroll-snap-type: y mandatory;
}

body {
    margin: 0;
    scroll-snap-type: y mandatory;
  }
  
  section {
    font-family: sans-serif;
    font-size: 12ch;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    scroll-snap-align: start; 
  }
 
  <head>

    <!-- MAIN STYLE -->
    <link rel="stylesheet" href="style.css">
    

  </head>

    <main>
     <section>
        <h1>Hello!</h1>
     </section>

     <section>
        <h1>Toodles~</h1>
     </section>

     <section>
        <h1>Boodles~</h1>
     </section>
            
    </main>

</html> 

【问题讨论】:

  • 回答主要问题:你需要 JS。

标签: javascript html css scroll-snap


【解决方案1】:

您可以监听滚动事件,并根据滚动方向将scroll-snap-type属性设置为html元素。

//save initial scrollY position
var previousScrollY = window.scrollY;
//listen to scroll event
window.addEventListener("scroll", function(e) {
  //is the user scrolling down ?
  let down = window.scrollY > previousScrollY;
  //set scroll-snap-type according to scroll direction
  document.documentElement.style.scrollSnapType = down ? "y mandatory" : "";
  //save current scrollY position
  previousScrollY = window.scrollY;
});
body {
  margin: 0;
}
  
section {
  font-family: sans-serif;
  font-size: 12ch;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  scroll-snap-align: start; 
}
<section>
    <h1>Hello!</h1>
 </section>

 <section>
    <h1>Toodles~</h1>
 </section>

 <section>
    <h1>Boodles~</h1>
 </section>

【讨论】:

  • 很好的例子,但是如果你玩演示的时间足够长(在整页中,当文档高度像 sn-p 那样小时不要说话)似乎是一个非常糟糕的 UI,导致用户体验不佳。
【解决方案2】:

不幸的是,当滚动方向改变时,您需要 JavaScript 来改变滚动行为。

? 我不是 100% 确定它为什么有效......

我立刻想到:

  • 将“滚动”事件注册到window
  • scroll-snap-type: y mandatory分配给&lt;body&gt;
  • 并与 window.scrollY? 进行比较。

这个以及源自herehere 的许多其他组合让我无法理解。进一步挖掘后,CSS 属性scroll-behavior 不会从&lt;body&gt; 传播到视口(又名window)——相反,当“滚动”事件发生时,实际上会检测到&lt;html&gt; 标签触发。尽管scroll-behavior 用于从链接滚动,但它是引导我走向正确方向的唯一线索,而且我无法找到一个真正好的解释,说明为什么 CSS 滚动似乎仅依赖于根。我相信由于内容占据了整个视口,window 是绑定事件处理程序的最佳选择,并且 CSS 滚动不会以标准方式冒泡。

&lt;html&gt;
?document.body.getBoundingClientRect().top


详情见下文注释

注意: Stack Snippet 所在的 iframe 与实际尺寸完全不接近。要获取真实尺寸(如视口)和相关行为(如滚动),请在 Full page

中查看

<!DOCTYPE html>
<html lang="en">

<head>
  <title></title>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <style>
     :root {
      font-family: 'Georgia';
      font-size: 62.5%;
      line-height: 1;
      scroll-behavior: smooth;
    }
    
    html,
    body {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
    }
    
    section {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      font-size: 1.6rem;
      outline: 5px dashed tomato;
      scroll-snap-align: start;
    }
    
    section:nth-of-type(odd) {
      color: gold;
      background: navy;
    }
    
    section:nth-of-type(even) {
      color: lime;
      background: black;
    }
    
    h1 {
      font-size: 6em;
      margin: 0;
    }
  </style>
</head>

<body>
  <section><h1>I</h1></section>
  <section><h1>II</h1></section>
  <section><h1>III</h1></section>
  <section><h1>IV</h1></section>
  <section><h1>V</h1></section>
  <section><h1>VI</h1></section>
  <section><h1>VII</h1></section>
  <section><h1>VIII</h1></section>
  <section><h1>IX</h1></section>
  <section><h1>X</h1></section>

  <script>
/*
Declare an initial value outside of event handler so that it is retained
after the function is done. 
*/
 let start = 0;
 // Bind window to the scroll event
 window.addEventListener('scroll', function() {
   // Reference the BCR of <body>
   const bcr = document.body.getBoundingClientRect();
   /*
   Get the distance from the top of viewport and the top of the body
   */
   const stop = bcr.top;
   /*
   If that distance greater than start, disable scroll-snap-type on <html>
   */
   if (stop > start) {
     document.scrollingElement.style.setProperty('scroll-snap-type', 'none');
   //console.log('up')
   } else {
   // Otherwise, enable scroll-snap-type
     document.scrollingElement.style.setProperty('scroll-snap-type', 'y mandatory');
   //console.log('down');
   }
   // Set start as stop for the next scroll
   start = stop;
 });
 </script>
</body>

</html>

【讨论】:

    【解决方案3】:

    根据我的研究,没有使用 CSS 的解决方案,为了实现这一点,请使用 javascript 窗口滚动事件并在向上滚动时删除 scrollSnapType CSS 属性。

    【讨论】:

      【解决方案4】:

      是的,您需要一点 javascript 来实现您想要的功能。这是一个优雅的问题解决方案:

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Scroll Snap</title>
          <style>
              *, ::after, ::before {
                  margin: 0;
                  box-sizing: border-box;
              }
              body {
                  height: 100vh;
                  overflow: hidden;
                  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
                  scroll-behavior: smooth;
              }
              .container {
                  height: 100vh;
                  overflow: hidden scroll;
              }
              .container.snap-scroll {
                  scroll-snap-type: y mandatory;
              }
              section {
                  height: 100vh;
                  font-size: 3rem;
                  display: flex;
                  justify-content: center;
                  align-items: center;
                  scroll-snap-align: start;
              }
              section {
                  font-weight: bold;
              }
              section span {
                  background-color: #80808020;
                  margin-left: 1rem;
                  padding: .25rem 1rem;
                  border-radius: .25rem;
                  color: #161616;
              }
              .bg-color-bisque {
                  background-color: bisque;
              }
              .bg-color-white {
                  background-color: white;
              }
              .bg-color-burlywood {
                  background-color: rgb(25, 226, 209);
              }
          </style>
      </head>
      <body>
          <div class="container snap-scroll">
              <section class="bg-color-bisque">Section <span>1</span></section>
              <section class="bg-color-white">Section <span>2</span></section>
              <section class="bg-color-burlywood">Section <span>3</span></section>
          </div>
          <script>
              let container = document.querySelector('.container');
              let lastScrollTop = 0;
              
              container.addEventListener('scroll', checkScrollDirection)
      
              function checkScrollDirection(event) {
                  let currentPosition = container.scrollTop;
                  if (currentPosition > lastScrollTop){
                      // On scroll down
                      container.classList.add('snap-scroll')
                  } else {
                      // On scroll up
                      container.classList.remove('snap-scroll')
                  }
                  lastScrollTop = currentPosition <= 0 ? 0 : currentPosition;
              }
          </script>
      </body>
      </html>
      

      【讨论】:

      • 这是车轮事件,不是滚动?
      • 是的,你是对的。让我重构。
      猜你喜欢
      • 2011-06-07
      • 2019-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-07
      • 2019-07-14
      • 2021-12-04
      相关资源
      最近更新 更多