IntersectionObserver does not work as intended on my element in React. As you can see in code I have threshold set to 0.5 but animations are fired as soon as they appear on the screen and I do not understand why. Can someone explain? (I've included CSS just in case that has something to do with it because I'm not sure.)
Demo video
import { buttonHoverAnimation, rotateAnimation } from "../../animations/animations";
import backgroundImage from "../../assets/images/pexels.jpg";
import smileIcon from "../../assets/icons/smile.svg";
import "./ExploreCard.css";
import { useEffect, useRef } from "react";
import { slideAnimation } from "../../animations/animations";
type ExploreCardProps = {
reverse?: boolean;
};
function ExploreCard({ reverse }: ExploreCardProps) {
const buttonHover = buttonHoverAnimation({}, {}, 30);
const contentRef = useRef<HTMLDivElement>(null);
const imageRef = useRef<HTMLDivElement>(null)
// Handler Slide animation
useEffect(() => {
if (!contentRef.current) return;
const animation = slideAnimation(
contentRef.current,
reverse ?
[-1000, 0] : [1000, 0]
);
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
animation.play();
observer.unobserve(entries[0].target);
}
}, { threshold: 0.5 });
observer.observe(contentRef.current);
return () => {
observer.disconnect();
};
});
// Handle rotate animation ,
useEffect(() => {
if (!imageRef.current) return;
const animation = rotateAnimation(imageRef.current)
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
animation.play();
observer.unobserve(entries[0].target);
}
},
{ threshold: 0.5 }
);
observer.observe(imageRef.current);
return () => {
observer.disconnect();
};
}, []);
return (
<div className={`explore-card ${reverse ? "reverse" : ""}`}>
<div className="explore-image-wrapper" ref={imageRef}>
<img src={backgroundImage} alt="Background-image" />
</div>
<div className={`explore-card-popup ${reverse ? "reverse" : ""}`}>
<h2>2X</h2>
<p>Faster</p>
</div>
<div className={`explore-content-wrapper ${reverse ? "reverse" : ""}`}ref={contentRef}>
<div className="explore-content-icon">
<img src={smileIcon} alt=":)" />
<p>QUICK</p>
</div>
<div className="explore-content-header">
<h1 className="explore-content-header-bold">HOME</h1>
<h1 className="explore-content-header-italic">Refresh</h1>
</div>
<div className="explore-content-description">
Experience the ultimate expert support — Creating spaces where comfort
and productivity thrive.
</div>
<div className="explore-content-button">
<button
onMouseOver={buttonHover.handleMouseIn}
onMouseOut={buttonHover.handleMouseOut}
>
<span>EXPLORE</span>
<span>EXPLORE</span>
</button>
</div>
</div>
</div>
);
}
export default ExploreCard;
@font-face {
font-family: intern-light;
src: url('../../assets/fonts/static/Inter_18pt-Thin.ttf');
}
@font-face {
font-family: intern-regular;
src: url('../../assets/fonts/static/Inter_18pt-Light.ttf');
}
@font-face {
font-family: intern-bold;
src: url('../../assets/fonts/static/Inter_24pt-Black.ttf');
}
@font-face {
font-family: intern-italic;
src: url('../../assets/fonts/static/Inter_18pt-ThinItalic.ttf');
}
.explore-card {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
position: relative;
gap: 2vw;
margin-top: 15vh;
overflow: hidden;
height: 50vh;
}
.explore-content-wrapper {
width: 35%;
display: flex;
padding: 7%;
display: inline-block;
}
.explore-content-icon > p{
font-family: intern-regular;
margin-left: 10px;
font-weight: 500;
letter-spacing: 2px;
}
.explore-content-icon {
display: flex;
}
.explore-content-icon > img {
width: 30px;
}
.explore-image-wrapper {
display: flex;
height: 50vh;
width: 25%;
border-radius: 70px;
overflow: hidden;
position: relative;
display: flex;
justify-content: center;
align-items: center;
z-index: 2;
}
.explore-image-wrapper > img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.explore-card-popup {
top: 10%;
left: 30%;
border-radius: 50px;
position: absolute;
height: 34%;
width: 13%;
background-color: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
-webkit-box-shadow: 0px 10px 50px -10px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 0px 10px 50px -10px rgba(0, 0, 0, 0.75);
box-shadow: 0px 10px 50px -10px rgba(0, 0, 0, 0.75);
z-index: 3;
}
.explore-card-popup > h2 {
font-family: intern-regular;
font-weight: 100;
font-size: 30px;
margin: 0;
}
.explore-card-popup > p {
font-family: intern-light;
font-size: 20px;
font-weight: 600;
margin-top: 0;
letter-spacing: 2px;
}
.explore-content-header {
display: flex;
font-size: 60px;
}
.explore-content-header > h1 {
margin-top: 5px;
margin-right: 10%;
margin-bottom: 5px;
}
.explore-content-header-bold {
font-family: intern-regular;
font-weight: 300;
}
.explore-content-header-italic {
font-family: intern-italic;
font-weight: 100;
}
.explore-content-description {
font-family: intern-regular;
font-size: 18px;
letter-spacing: 2px;
line-height: 30px;
margin-bottom: 30px;
}
.explore-content-button > button {
font-family: intern-regular;
height: 1.3rem;
font-size: 1.3rem;
background: none;
border: 0;
cursor: pointer;
display: flex;
flex-direction: column;
overflow: hidden;
}
.explore-content-button > button > span {
position: relative;
transition: transform 0.7s, opacity 0.7s;
margin: 0;
}
.explore-card.reverse {
flex-direction: row-reverse;
}
.explore-card-popup.reverse {
left: 57%;
}
IntersectionObserver does not work as intended on my element in React. As you can see in code I have threshold set to 0.5 but animations are fired as soon as they appear on the screen and I do not understand why. Can someone explain? (I've included CSS just in case that has something to do with it because I'm not sure.)
Demo video
import { buttonHoverAnimation, rotateAnimation } from "../../animations/animations";
import backgroundImage from "../../assets/images/pexels.jpg";
import smileIcon from "../../assets/icons/smile.svg";
import "./ExploreCard.css";
import { useEffect, useRef } from "react";
import { slideAnimation } from "../../animations/animations";
type ExploreCardProps = {
reverse?: boolean;
};
function ExploreCard({ reverse }: ExploreCardProps) {
const buttonHover = buttonHoverAnimation({}, {}, 30);
const contentRef = useRef<HTMLDivElement>(null);
const imageRef = useRef<HTMLDivElement>(null)
// Handler Slide animation
useEffect(() => {
if (!contentRef.current) return;
const animation = slideAnimation(
contentRef.current,
reverse ?
[-1000, 0] : [1000, 0]
);
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
animation.play();
observer.unobserve(entries[0].target);
}
}, { threshold: 0.5 });
observer.observe(contentRef.current);
return () => {
observer.disconnect();
};
});
// Handle rotate animation ,
useEffect(() => {
if (!imageRef.current) return;
const animation = rotateAnimation(imageRef.current)
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
animation.play();
observer.unobserve(entries[0].target);
}
},
{ threshold: 0.5 }
);
observer.observe(imageRef.current);
return () => {
observer.disconnect();
};
}, []);
return (
<div className={`explore-card ${reverse ? "reverse" : ""}`}>
<div className="explore-image-wrapper" ref={imageRef}>
<img src={backgroundImage} alt="Background-image" />
</div>
<div className={`explore-card-popup ${reverse ? "reverse" : ""}`}>
<h2>2X</h2>
<p>Faster</p>
</div>
<div className={`explore-content-wrapper ${reverse ? "reverse" : ""}`}ref={contentRef}>
<div className="explore-content-icon">
<img src={smileIcon} alt=":)" />
<p>QUICK</p>
</div>
<div className="explore-content-header">
<h1 className="explore-content-header-bold">HOME</h1>
<h1 className="explore-content-header-italic">Refresh</h1>
</div>
<div className="explore-content-description">
Experience the ultimate expert support — Creating spaces where comfort
and productivity thrive.
</div>
<div className="explore-content-button">
<button
onMouseOver={buttonHover.handleMouseIn}
onMouseOut={buttonHover.handleMouseOut}
>
<span>EXPLORE</span>
<span>EXPLORE</span>
</button>
</div>
</div>
</div>
);
}
export default ExploreCard;
@font-face {
font-family: intern-light;
src: url('../../assets/fonts/static/Inter_18pt-Thin.ttf');
}
@font-face {
font-family: intern-regular;
src: url('../../assets/fonts/static/Inter_18pt-Light.ttf');
}
@font-face {
font-family: intern-bold;
src: url('../../assets/fonts/static/Inter_24pt-Black.ttf');
}
@font-face {
font-family: intern-italic;
src: url('../../assets/fonts/static/Inter_18pt-ThinItalic.ttf');
}
.explore-card {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
position: relative;
gap: 2vw;
margin-top: 15vh;
overflow: hidden;
height: 50vh;
}
.explore-content-wrapper {
width: 35%;
display: flex;
padding: 7%;
display: inline-block;
}
.explore-content-icon > p{
font-family: intern-regular;
margin-left: 10px;
font-weight: 500;
letter-spacing: 2px;
}
.explore-content-icon {
display: flex;
}
.explore-content-icon > img {
width: 30px;
}
.explore-image-wrapper {
display: flex;
height: 50vh;
width: 25%;
border-radius: 70px;
overflow: hidden;
position: relative;
display: flex;
justify-content: center;
align-items: center;
z-index: 2;
}
.explore-image-wrapper > img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.explore-card-popup {
top: 10%;
left: 30%;
border-radius: 50px;
position: absolute;
height: 34%;
width: 13%;
background-color: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
-webkit-box-shadow: 0px 10px 50px -10px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 0px 10px 50px -10px rgba(0, 0, 0, 0.75);
box-shadow: 0px 10px 50px -10px rgba(0, 0, 0, 0.75);
z-index: 3;
}
.explore-card-popup > h2 {
font-family: intern-regular;
font-weight: 100;
font-size: 30px;
margin: 0;
}
.explore-card-popup > p {
font-family: intern-light;
font-size: 20px;
font-weight: 600;
margin-top: 0;
letter-spacing: 2px;
}
.explore-content-header {
display: flex;
font-size: 60px;
}
.explore-content-header > h1 {
margin-top: 5px;
margin-right: 10%;
margin-bottom: 5px;
}
.explore-content-header-bold {
font-family: intern-regular;
font-weight: 300;
}
.explore-content-header-italic {
font-family: intern-italic;
font-weight: 100;
}
.explore-content-description {
font-family: intern-regular;
font-size: 18px;
letter-spacing: 2px;
line-height: 30px;
margin-bottom: 30px;
}
.explore-content-button > button {
font-family: intern-regular;
height: 1.3rem;
font-size: 1.3rem;
background: none;
border: 0;
cursor: pointer;
display: flex;
flex-direction: column;
overflow: hidden;
}
.explore-content-button > button > span {
position: relative;
transition: transform 0.7s, opacity 0.7s;
margin: 0;
}
.explore-card.reverse {
flex-direction: row-reverse;
}
.explore-card-popup.reverse {
left: 57%;
}
Share
Improve this question
edited Mar 27 at 13:26
isherwood
61.2k16 gold badges121 silver badges170 bronze badges
asked Mar 27 at 13:23
Zura GabichvadzeZura Gabichvadze
666 bronze badges
0
1 Answer
Reset to default 0After 2 days of searching, I didn't find any solution so, I just use react-hook-observer and threshold works as intended, I do not know whether or not it's react thing that threshold doesn't work but I couldn't find any answers online. So for now I am happy to share that everything works much better.