I would like to have div
elements fade in and out when scrolling over them by changing the opacity. It sounds really simple but can't get it to work.
The problem I have is that the div
are in the middle of my page and not on top, so scrollTop
shouldn't work right?
var fade = $('.b_wrapper');
var range = 400;
$(window).on('scroll', function() {
var st = $(this).scrollTop();
fade.each(function() {
var offset = $(this).offset().top;
var height = $(this).outerHeight();
offset = offset height / 1;
$(this).css({
'opacity': 1 - (st offset - range) / range
});
});
});
.content {
height: 100px;
}
.b_wrapper {
background: lightgreen;
margin-top: 40px;
padding: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<div ></div>
<div >
<p>this is a div</p>
</div>
<div >
<p>this is a div</p>
</div>
<div >
<p>this is a div</p>
</div>
<div >
<p>this is a div</p>
</div>
<div >
<p>this is a div</p>
</div>
<div >
<p>this is a div</p>
</div>
<div >
<p>this is a div</p>
</div>
Little demo to hopefully make my question more clear: https://jsfiddle.net/gLnwy6h3/2/
CodePudding user response:
You can use the Intersection Observer API. It watches for changes in the intersection between a target element and its ancestor element or the viewport.
First, create a new observer with options:
let options = {
root: null,
threshold: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
}
let observer = new IntersectionObserver(callback, options);
root
specifies either the ancestor (containing) element, e.g. withdocument.querySelector()
, or the viewport withnull
.threshold
can take an array of intersection thresholds to watch for, which we'll directly use as opacity for our elements. So each time an element's intersection ratio with the viewport reaches one of these values, the callback is triggered. (You can use a function here to build the array if you want a more fine-grained sequence and don't want to write everything by hand.)
Next, add each element to the observer:
for (const target of document.querySelectorAll('.b_wrapper')) {
observer.observe(target);
}
The callback is very simple, as we can directly use the provided intersectionRatio
as opacity:
let callback = (entries, observer) => {
entries.forEach((entry) => {
entry.target.style.opacity = entry.intersectionRatio
});
};
So much to get you started. Read the documentation, it's excellent and has many examples.
let callback = (entries, observer) => {
entries.forEach((entry) => {
entry.target.style.opacity = entry.intersectionRatio
});
};
let options = {
root: null,
threshold: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
}
let observer = new IntersectionObserver(callback, options);
for (const target of document.querySelectorAll('.b_wrapper')) {
observer.observe(target);
}
.b_wrapper{
margin: 20px;
padding: 20px;
background: lightgreen;
}
<div ><p>this is a div</p></div>
<div ><p>this is a div</p></div>
<div ><p>this is a div</p></div>
<div ><p>this is a div</p></div>
<div ><p>this is a div</p></div>
<div ><p>this is a div</p></div>
<div ><p>this is a div</p></div>
<div ><p>this is a div</p></div>
CodePudding user response:
Probably not the best but here is my try :
On scroll, I check the position of every element to see wich one is the most centered
On window resize, I update the center of screen value
let centerOfScreen = window.innerHeight / 2;
let pageScroll;
let closestCenteredDivPosition = 999999;
let currentIndex;
let wrappers = document.getElementsByClassName("wrapper");
let positions = [];
for (let e of wrappers) {
positions.push(e.offsetTop e.offsetHeight / 2);
}
highlightMostCenteredDiv = () => {
pageScroll = document.documentElement.scrollTop || document.body.scrollTop;
let currentValue;
for (let i = 0; i < positions.length; i ) {
currentValue = Math.abs(positions[i] - centerOfScreen - pageScroll);
if (closestCenteredDivPosition > currentValue) {
closestCenteredDivPosition = currentValue;
currentIndex = i;
}
}
if (!document.querySelector(".current")) {
wrappers[currentIndex].classList.add("current");
} else {
if (wrappers[currentIndex] != document.querySelector(".current")) {
document.querySelector(".current").classList.remove("current");
wrappers[currentIndex].classList.add("current");
}
}
closestCenteredDivPosition = 999999;
}
highlightMostCenteredDiv();
document.addEventListener("scroll", highlightMostCenteredDiv)
window.addEventListener("resize", () => {
centerOfScreen = window.innerHeight / 2;
})
body {
margin: 0;
}
.wrapper {
background-color: orange;
height: 100px;
width: 100%;
margin: 50px 0;
opacity: 0.3;
transition: opacity 1s ease;
}
.current {
opacity: 1;
}
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>