代码之家  ›  专栏  ›  技术社区  ›  Somnium

与IntersectionObserver一起使用时,粘滞DIV会闪烁

  •  0
  • Somnium  · 技术社区  · 2 年前

    我有黏糊糊的 div 和使用 IntersectionObserver 检测粘滞何时被固定到视口顶部,并为其添加阴影。问题是,阴影本身会以某种方式扰乱交点检测,导致阴影闪烁。我用 filter: drop-shadow 为了阴影。使用 box-shadow 效果很好,但我不能使用它,因为在我的例子中,粘性是非矩形的。

    这里有一个例子。交集观察器在代码段iframe中无法正常工作,但您可以通过单击“运行代码段”、“整页”、右键单击“另存为”,然后打开保存的HTML来重复闪烁。

    const ob = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          entry.target.classList.toggle('pinned', entry.intersectionRatio < 1);
        }
      },
      {
        threshold: [1],
        rootMargin: '-1px 0px 0px 0px',
      },
    );
    ob.observe(document.getElementById('sticky'));
    #sticky {
      position: sticky;
      top: 0;
      background-color: khaki;
    }
    .pinned {
      filter: drop-shadow(0px 3px 3px red);
      /* this is fine: */
      /* box-shadow: 0px 3px 3px red; */
    }
    
    /* added by editor for demo purpose */
    body {
      min-height: 500vh;
    }
    
    div[data-placeholder="A"] {
      height: 20vh;
    }
    <div data-placeholder="A"></div>
    <div id="sticky">
      I am sticky
    </div>

    如何解决这个问题?

    0 回复  |  直到 2 年前
        1
  •  3
  •   A Haworth    2 年前

    虽然我不能完全重现问题中描述的问题,但我可以看到,观察和切换同一个元素可能会产生闪烁,元素会稍微上下移动。

    为了解决这个问题,这个代码段添加了一个“传感器”像素元素,即被观察到的元素,而不是粘性元素。当它移出视口时,固定的类被添加到粘滞元素中,当它返回视口时固定的类会被删除。这样,交叉观察和粘性就分开了。没有闪烁。

    <style>
      #sensor {
        position: absolute;
        width: 1px;
        height: 1px;
      }
      
      #sticky {
        position: sticky;
        top: 0px;
        background-color: khaki;
      }
      
      .pinned {
        filter: drop-shadow(0px 3px 3px red);
        /* this is fine: */
        /* box-shadow: 0px 3px 3px red; */
      }
      
      body {
        /* just for demo to make sure we can scroll */
        min-height: 200vh;
      }
    </style>
    
    <body>
      <div>line<br>line<br>line<br>line<br>line<br>line<br>line<br></div>
      <div id="sensor"></div>
      <div id="sticky">
        I am sticky
      </div>
      <script>
        const ob = new IntersectionObserver(
          (entries) => {
            for (const entry of entries) {
              if (entry.isIntersecting) {
                document.querySelector('#sticky').classList.remove('pinned');
              } else {
                document.querySelector('#sticky').classList.add('pinned');
              }
            }
          });
        ob.observe(document.querySelector('#sensor'));
      </script>
    </body>