最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to make a React component fade in on scroll using IntersectionObserver, but only once? - Stack Overflow

programmeradmin0浏览0评论

I am trying to give components a fade-in effect in React when the user scrolls, but I want the fade-in effect to only happen the first time the element moves into the viewport.

Currently, the code I am using causes a fade-in every time the element moves into the viewport, so they are constantly fading in and out.

Here is my fade-in component:

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>
  )
}

And these are the styles I'm using:

.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; 
}

Here is my website, which keeps fading components in and out, offering a terrible experience:

And this is the desired effect:

How can I achieve the desired effect?

Here is a link to the code sandbox to test it: Code sandbox link

I am trying to give components a fade-in effect in React when the user scrolls, but I want the fade-in effect to only happen the first time the element moves into the viewport.

Currently, the code I am using causes a fade-in every time the element moves into the viewport, so they are constantly fading in and out.

Here is my fade-in component:

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>
  )
}

And these are the styles I'm using:

.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; 
}

Here is my website, which keeps fading components in and out, offering a terrible experience:

And this is the desired effect:

How can I achieve the desired effect?

Here is a link to the code sandbox to test it: Code sandbox link

Share Improve this question edited Jan 5, 2020 at 19:25 klaurtar1 asked Jan 4, 2020 at 23:22 klaurtar1klaurtar1 7782 gold badges10 silver badges34 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 13

You only need to call setVisible if entry.isIntersecting is true, so simply replace:

setVisible(entry.isIntersecting);

With:

entry.isIntersecting && setVisible(true);

This way, once an entry has already been marked as visible, it won't be unmarked, even if you scroll back up, so the element goes out of the viewport, and entry.isIntersecting becomes false again.

Actually, you can even call observer.unobserve at that point, as you don't care anymore.

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.disconnect();
  }, []);

  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'));
body {
  font-family: monospace;
  margin: 0;
}

section {
  padding: 16px;
  margin: 16px;
  box-shadow: 0 0 8px rgba(0, 0, 0, .125);
  height: 64px;
  opacity: 0;
  transform: translate(0, 50%);
  visibility: hidden;
  transition: opacity 300ms ease-out, transform 300ms ease-out;
  will-change: opacity, visibility;
}

.is-visible {
  opacity: 1;
  transform: none;
  visibility: visible;
  display: flex; 
}
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论