如何使用 IntersectionObserver 使 React 组件在滚动时淡入淡出,但只有一次?
How to make a React component fade in on scroll using IntersectionObserver, but only once?
我试图在用户滚动时在 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>
)
}
这些是我正在使用的样式:
.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;
}
这是我的网站,它不断淡入淡出组件,提供糟糕的体验:
这是想要的效果:
怎样才能达到想要的效果?
这里有一个link代码沙箱来测试一下:Code sandbox link
如果entry.isIntersecting
是true
,你只需要调用setVisible
,所以只需替换:
setVisible(entry.isIntersecting);
有:
entry.isIntersecting && setVisible(true);
这样,一旦一个条目已经被标记为可见,它就不会被取消标记,即使你向上滚动,所以元素离开视口,entry.isIntersecting
变成 false
再次。
实际上,您甚至可以在那个时候调用 observer.unobserve
,因为您不再关心了。
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'));
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/react@16.12.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.12.0/umd/react-dom.development.js"></script>
<div id="app"></div>
我试图在用户滚动时在 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>
)
}
这些是我正在使用的样式:
.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;
}
这是我的网站,它不断淡入淡出组件,提供糟糕的体验:
这是想要的效果:
怎样才能达到想要的效果?
这里有一个link代码沙箱来测试一下:Code sandbox link
如果entry.isIntersecting
是true
,你只需要调用setVisible
,所以只需替换:
setVisible(entry.isIntersecting);
有:
entry.isIntersecting && setVisible(true);
这样,一旦一个条目已经被标记为可见,它就不会被取消标记,即使你向上滚动,所以元素离开视口,entry.isIntersecting
变成 false
再次。
实际上,您甚至可以在那个时候调用 observer.unobserve
,因为您不再关心了。
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'));
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/react@16.12.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.12.0/umd/react-dom.development.js"></script>
<div id="app"></div>