如何使用 IntersectionObserver 使 React 组件在滚动时淡入淡出,但只有一次?

kla*_*ar1 6 javascript scroll fadein reactjs intersection-observer

当用户滚动时,我试图在 React 中为组件提供淡入效果,但我希望淡入效果仅在元素第一次移动到视口时发生。

目前,每次元素移动到视口时,我使用的代码都会导致淡入,因此它们不断地淡入淡出。

这是我的淡入组件:

import React, {useState, useRef, useEffect} from 'react';
import './styles/FadeInSection.css';

export default function FadeInSection(props) {
  const [isVisible, setVisible] = useState(true);

  const domRef = React.useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => setVisible(entry.isIntersecting));
    });

    observer.observe(domRef.current);

    return () => observer.unobserve(domRef.current);
  }, []);

  return (
    <div ref={ domRef } className={ `fade-in-section ${ isVisible ? 'is-visible' : '' }` }>
      { props.children }
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

这些是我正在使用的样式:

.fade-in-section {
  opacity: 0;
  transform: translateY(20vh);
  isibility: hidden;
  transition: opacity 0.2s ease-out, transform 0.6s ease-out;
  will-change: opacity, visibility;
}

.fade-in-section.is-visible {
  opacity: 1;
  transform: none;
  visibility: visible;
  display: flex; 
}
Run Code Online (Sandbox Code Playgroud)

这是我的网站,它不断地淡入淡出组件,提供了糟糕的体验:

我的网站

这是想要的效果:

甜美的淡入效果

我怎样才能达到预期的效果?

这是代码沙箱的链接以进行测试:代码沙箱链接

Dan*_*ger 5

你只需要调用setVisibleif entry.isIntersectingis true,所以只需替换:

setVisible(entry.isIntersecting);
Run Code Online (Sandbox Code Playgroud)

和:

entry.isIntersecting && setVisible(true);
Run Code Online (Sandbox Code Playgroud)

这样,一旦一个条目已被标记为可见,即使您向上滚动,它也不会被取消标记,因此该元素会离开视口,然后再次entry.isIntersecting变为false

事实上,你甚至可以observer.unobserve在那个时候打电话,因为你不再关心了。

setVisible(entry.isIntersecting);
Run Code Online (Sandbox Code Playgroud)
entry.isIntersecting && setVisible(true);
Run Code Online (Sandbox Code Playgroud)
const FadeInSection = ({
  children,
}) => {
  const domRef = React.useRef();
  
  const [isVisible, setVisible] = React.useState(false);

  React.useEffect(() => {
    const observer = new IntersectionObserver(entries => {
      // In your case there's only one element to observe:     
      if (entries[0].isIntersecting) {
      
        // Not possible to set it back to false like this:
        setVisible(true);
        
        // No need to keep observing:
        observer.unobserve(domRef.current);
      }
    });
    
    observer.observe(domRef.current);
    
    return () => observer.unobserve(domRef.current);
  }, []);

  return (<section ref={ domRef } className={ isVisible ? ' is-visible' : '' }>{ children }</section>);
};

const App = () => {  
  const items = [1, 2, 3, 4, 5, 6, 7, 8].map(number => (
    <FadeInSection key={ number }>Section { number }</FadeInSection>
  ));

  return (<main>{ items }</main>);
}

ReactDOM.render(<App />, document.querySelector('#app'));
Run Code Online (Sandbox Code Playgroud)