Dependencies
- CodyFrame (CodyHouse front-end framework)
Tutorial
This effect uses the sticky
value of the CSS position property. When using the sticky position, we generally set a top
to the sticky element; the element initially behaves like a relative position element (normal scroll) until it reaches a specified top position and becomes fixed.
For the Revealing Hero, we want to create the opposite effect: we want the element to be fixed at the beginning and then starts scrolling (like a relative position element) once its content has been revealed. This can be achieved setting a bottom
value (rather than top
) in combo with the position sticky.
If we set a bottom of 0px, though, part of the section content may not be visible (it behaves like a fixed element with a bottom of 0px). We can move the element along the Y axis using a negative bottom value. The new bottom value will be equal to the difference between its height and the window height.
This tutorial was inspired by Claudio Guglieri's portfolio.
<section class="position-relative zindex-2 bg shadow-sm">
<!-- section with a normal flow -->
</section>
<section class="revealing-hero bg zindex-1 js-revealing-hero">
<!-- Revealing Hero content -->
</section>
In CSS, we'll need to set the position value:
.revealing-hero {
position: sticky;
min-height: 100vh;
}
We can set the bottom value in JavaScript:
var revealingHero = document.getElementsByClassName('js-revealing-hero')[0];
revealingHero.style.bottom = (window.innerHeight - revealingHero.offsetHeight)+'px';
Now that we have our scrolling behaviour in place, we can improve the effect adding an overlay with an opacity value that changes on scrolling. We'll use an ::after
pseudo element to create the effect:
.revealing-hero {
--reavealing-hero-overlay-opacity: 1; // overlay layer opacity - modified using JS
&::after { // overlay layer
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
pointer-events: none;
background-color: var(--color-black);
opacity: var(--reavealing-hero-overlay-opacity);
}
}
The opacity of the ::after
element is set using the --reavealing-hero-overlay-opacity
CSS custom property. This value can be changed on scroll (from 1 to 0) using the Intersection Observer API.
We can create a RevealingHero
object that we use to initialize the effect:
var RevealingHero = function(element) {
this.element = element;
this.scrollingFn = false;
this.scrolling = false;
initRevealingHero(this);
};
function initRevealingHero(element) {
createTopElement(element); // create a new node - to be inserted before the sticky element
initRevealingHeroEffect(element); // change the ::after element opacity on scroll
};
The createTopElement
function is used to create an hidden element that is inserted right before the Revealing Hero section. The Intersection Observer can be used to detect when this element enters the viewport to update the --reavealing-hero-overlay-opacity
value:
function initRevealingHeroEffect(element) {
var observer = new IntersectionObserver(revealingHeroCallback.bind(element));
observer.observe(element.prevElement);
};
function revealingHeroCallback(entries) {
if(entries[0].isIntersecting) {
if(this.scrollingFn) return; // listener for scroll event already added
this.scrollingFn = revealingHeroScrolling.bind(this);
window.addEventListener('scroll', this.scrollingFn);
} else {
if(!this.scrollingFn) return; // listener for scroll event already removed
window.removeEventListener('scroll', this.scrollingFn);
this.scrollingFn = false;
}
};
function revealingHeroScrolling() {
if(this.scrolling) return;
this.scrolling = true;
window.requestAnimationFrame(animateRevealingHero.bind(this));
};
function animateRevealingHero() {
// change opacity value
};
When element.prevElement
is inside the viewport (entries[0].isIntersecting == true
in revealingHeroCallback
function), we listen to the window scroll event and update the CSS property value accordingly.