javascript - CSS-animationer som aktiveras vid scrollning via IntersectionObserver fungerar inte korrekt i React
Jag försökte animera en webbsida och övervägde att använda rullningsfunktionen på en webbsida för att aktivera nämnda animationer. Jag följde en handledning som använde JavaScript och inget extra bibliotek för att göra detta via följande kod
function App() {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("show");
} else {
entry.target.classList.remove("show");
}
});
});
const hiddenElements = document.querySelectorAll(".hidden");
hiddenElements.forEach((el) => observer.observe(el));
return (
<div>
<p className="sample">Sample hello Text</p>
<p>
<br />
<br />
<br />
<br />
</p>
<p className="hidden">Instruction 1</p>
<p>
<br />
<br />
<br />
<br />
</p>
<p className="hidden">Instruction 2</p>
<p>
<br />
<br />
<br />
<br />
</p>
<p className="hidden">Instruction 3</p>
<p>
<br />
<br />
<br />
<br />
</p>
<div>
<img className="hidden" src="src/assets/react.svg" />
</div>
</div>
);
}
export default App;
Observera: Detta är bara testkod och brytningstaggarna användes, så det finns innehåll att scrolla till.
Här är den medföljande CSS-koden:
body {
background-color: gray;
}
div {
display: grid;
place-items: center;
align-items: center;
min-height: 100vh;
}
p {
font-size: 30px;
font-style: bold;
color: black;
}
.hidden {
transition: all 1.5s;
width: 50px;
opacity: 0.5;
filter: blur(5px);
transform: translateX(-100%);
}
.show {
opacity: 1;
filter: blur(0);
transform: translateX(0);
}
/*
// Same animations but using keyframes and no additional code to execute upon scrolling, this works // perfectly
.hidden {
transition: all 1.5s;
transform: translate(-150%);
animation: thing 1s linear both;
}
@keyframes thing {
0% {
transform: translate(-150%);
opacity: 0.5;
filter: blur(5px);
}
100% {
transform: translate(0);
opacity: 1;
filter: blur(0);
}
} */
Nu i handledningen gjorde de detta med en HTML- och CSS-fil, men här gjorde jag detta med React tillsammans med Vite och Typescript. Nu händer detta:
Animationen från .hidden till .show körs inte och den fastnar i .hidden-tillståndet, om jag inte sparar .tsx-filen som innehåller app-funktionen utan att redigera något. Vid uppdatering av sidan skulle samma problem uppstå igen och jag behövde spara filen på nytt för att det skulle fungera korrekt.
Så här ska det se ut efter att animationerna har körts:
Jag trodde också att eftersom texten Instruction1 och Instruction2 var synlig vid uppdatering, skulle animationerna köras eftersom de startar i skärningspunkten, men det led fortfarande av samma problem.
Jag reproducerade samma kod i en vanlig HTML- och JS-fil och koden fungerar. Jag gjorde om exakt samma animationer med keyframes utan intersectionObserver-koden och det fungerar, men det innebar att animationerna bara kördes i början av sidans laddning och inte när de var synliga.
Jag redigerade CSS-filen med transformationerna som riktar sig mot .hidden för att se om det var CSS-filen som orsakade felen, men när jag sparar några ändringar uppdateras webbsidan motsvarande .hidden-tillståndet, men animationen till .show sker inte om jag inte återigen sparar .tsx-filen.
Vet någon vad som orsakar problemet, är det eftersom det är Typescript eller React eller Vite, och hur man fixar detta?
Vilka andra sätt finns det också att animera vid scroll utan att använda ett externt bibliotek?
Tack.
Svar #1
Animationen fungerar inte eftersom du försöker observera komponenterna innan de ens har renderats. Ställ in IntersectionObserver i en useEffect, på följande sätt:
function App() {
React.useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("show");
} else {
entry.target.classList.remove("show");
}
});
});
const hiddenElements = document.querySelectorAll(".hidden");
hiddenElements.forEach((el) => observer.observe(el));
// Clean up the memory before the component gets destroyed:
return () => {
hiddenElements.forEach((el) => observer.unobserve(el));
};
}, []);
return (
<div>
<p className="sample">Scroll to see annimations</p>
<p className="hidden">Instruction 1</p>
<p className="hidden">Instruction 2</p>
<p className="hidden">Instruction 3</p>
</div>
);
}
// This is to have a working example here on Stack Overflow:
ReactDOM.render(
<App />,
document.getElementById("root")
);* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
padding: 1rem;
}
p {
background-color: lightgrey;
padding: 1rem;
border-radius: 1rem;
min-height: 70vh;
display: grid;
place-items: center;
font-family: sans-serif;
margin-bottom: 1rem;
}
p:first-of-type {
min-height: 100vh;
}
.hidden {
transition: all 1.5s;
opacity: 0.5;
filter: blur(5px);
transform: translateX(-100%);
}
.show {
opacity: 1;
filter: blur(0);
transform: translateX(0);
}<!-- This is to have a working example here on Stack Overflow: -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
