I want to create an animation for an element with following properties:
- it animates the element when it enters the viewport
- if the element left the viewport and then enters it again it should be animated again
- depending on the scroll direction/intersected side (from top or bottom) the animation should be different
For this purpose I use an IntersectionObserver
and I came close to the desired outcome.
The only problem I am facing is, when I translate the element in the scroll direction (which is in this case transform: translateY
) during the animation. This will cause the IntersectionObserver
to trigger multiple or even infinite times.
function isIntersectingFromTop(entry){
return entry.boundingClientRect.bottom != entry.intersectionRect.bottom;
}
function isIntersectingFromBottom(entry){
return entry.boundingClientRect.top != entry.intersectionRect.top;
}
var count = 0;
function incrementCounter(entry){
document.querySelector(".counter").textContent += "intersectionRation (" + count + "): " + entry.intersectionRatio + "\n";
count++;
}
let observer = new IntersectionObserver(
function (entries, observer) {
entries.forEach(function(entry){
incrementCounter(entry)
if (entry.isIntersecting) {
if(isIntersectingFromTop(entry)){
entry.target.classList.add("animated--up-in");
} else if(isIntersectingFromBottom(entry)) {
entry.target.classList.add("animated--down-in")
}
} else {
/** element is not in viewport anymore
* this will be triggered right after the animation starts
* since the translate is moving the elment out of the view
* which is causing a new intersection (isIntersecting = false)
*/
entry.target.classList.remove("animated--up-in");
entry.target.classList.remove("animated--down-in");
}
});
});
observer.observe(document.querySelector(".to-animate"));
.container {
height: 1000px;
width: 100%;
}
.box{
position: static;
width: 100px;
height: 100px;
background: red;
margin-top: 10px;
}
.to-animate{
background: blue;
opacity: 0;
}
.animated--up-in {
animation: animateUpIn 1.5s forwards ease;
}
.animated--down-in {
animation: animateDownIn 1.5s forwards ease;
}
@keyframes animateUpIn {
from {
transform: translateY(100px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes animateDownIn {
from {
transform: translateY(-100px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.counter {
position: fixed;
top: 10%;
left: 30%;
color: black;
height: 80%;
width: 50%;
overflow-y: auto;
padding: 10px;
}
<div class="container">
<pre class="counter"></pre>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box to-animate"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
Question
How can I "tell" the IntersectionObserver
to ignore translated position and just use the initial/original position to calculate intersections (with no extra element)? Is this even possible?