I am attempting to make an image on a website fade in and up when a user scrolls the image into the viewport. The code I have so far is below. However, when I run this code I get a 404 error output. Any assistance is appreciated! I am quite new to JS and have been trying to figure this out for a while.
Here is my CSS.
.section3 {
opacity: 0;
transform: translateY(20vh);
visibility: hidden;
transition: opacity 0.6s ease-out, transform 1.2s ease-out;
will-change: opacity, visibility;
}
.fade {
opacity: 1;
transform: none;
visibility: visible;
}
Below is the HTML and JS.
<section id="section3" >
<img style="width: 100%;" src="lovethyneighbor.jpg">
</section>
<script>
var section3 = document.getElementById("section3");
var location = section3.getBoundingClientRect();
if (location.top >= 0) {
document.getElementById("section3").classList.add("fade");
} else {
document.getElementById("section3").classList.add("section3");
}
</script>
CodePudding user response:
Introducing the Intersection Observer API! This is included in JavaScript and is a great tool for triggering an event when an element is in the viewport.
It's a really powerful tool and I'd highly suggest using this over getBoundingClientRect()
. One of the main reasons for this is with your code:
if (location.top >= 0) {
document.getElementById("section3").classList.add("fade");
}
else {
document.getElementById("section3").classList.add("section3");
}
You will have to run a function on every single mousewheel event, which is unreliable and can hurt performance. If you're using Intersection Observer, the API will "watch" your page and will run a function whenever the element is in the viewport. The code below is explained through inline comments.
Scalable with multiple elements that need different animations
// the sections/containers
const sections = document.querySelectorAll("section.section");
// options for the intersection function
const options = {
root: null,
threshold: 0.5, // how much of the element should be visible before the function is triggered? from 0 - 1
rootMargin: "0px 0px 0px 0px" // default rootmargin value
};
// the observer - with foreach we can trigger multiple elements with multiple animations if need be
let observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
// the element that is going to be animated
const block = entry.target.querySelector("img.fader");
// elements to be animated, e.g.
// if multiple elements with animations need to run inside the same section
const animationBlocks = entry.target.querySelectorAll("[data-animation]");
// when the element is triggered
if (entry.isIntersecting) {
// foreach, if multiple animations need to run on the same element
animationBlocks.forEach((animation) => {
animationClass = animation.dataset.animation;
// adding the data-animation class class to the element, so the animation can run
animation.classList.add(animationClass);
});
}
});
}, options);
observer.observe(document.querySelector("section.section"));
// running the animations
document.addEventListener("DOMContentLoaded", function() {
Array.from(sections).forEach(function(element) {
observer.observe(element);
});
});
body {
height: 300vh;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-color: teal;
gap: 400px;
}
/* starting values */
[data-animation="fadeInUp"] {
opacity: 0;
transform: translate3d(0, 20px, 0);
}
/* when classname is applied to the element, run the animation */
.fadeInUp {
animation-name: fadeInUp;
animation-duration: 0.6s;
animation-fill-mode: both;
}
/* the animation */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translate3d(0, 20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
<section id="section2" >
<img data-animation="fadeInUp" style="width: 100%;" src="https://picsum.photos/200/300">
</section>
<section id="section3" >
<img data-animation="fadeInUp" style="width: 100%;" src="https://picsum.photos/200/300">
</section>
Simple function with single element and single animation
const sections = document.querySelectorAll("section.section");
const options = {
root: null,
threshold: 0.5
};
let observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
const block = entry.target.querySelector("img.fader");
if (entry.isIntersecting) {
block.classList.add('fadeInUp');
}
});
}, options);
observer.observe(document.querySelector("section.section"));
// running the animations
document.addEventListener("DOMContentLoaded", function() {
Array.from(sections).forEach(function(element) {
observer.observe(element);
});
});
body {
height: 300vh;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-color: teal;
gap: 400px;
}
img.fader {
opacity: 0;
transform: translate3d(0, 20px, 0);
}
.fadeInUp {
animation-name: fadeInUp;
animation-duration: 0.6s;
animation-fill-mode: both;
}
/* the animation */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translate3d(0, 20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
<section id="section2" >
<img data-animation="fadeInUp" style="width: 100%;" src="https://picsum.photos/200/300">
</section>
<section id="section3" >
<img style="width: 100%;" src="https://picsum.photos/200/300">
</section>