I am trying to animate four headings as the user scrolls. First I create a sticky
positioned div, then the user scrolls over the headings toggling the class .active
one at a time, leaving the last one visible and continuing scrolling to the last div.
The problem I am having is that once the .sticky
div becomes sticky the browser no longer detects further scrolling until the div becomes non-sticky again.
--- UPDATE
I have changed the code to transition sequentially and works better, but I would like to start the animations only when the .sticky
div becomes sticky, but I tried that and the scroll animation stops working completely. Also, I would like to keep all headings on the same block but If I do position: absolute
on them it breaks the animations too.
const headings = Array.from(document.querySelectorAll('.animated-text'));
const sticky = document.querySelector('.sticky');
let currentActive = null;
window.addEventListener('scroll', () => {
const viewportHeight = window.innerHeight;
headings.forEach((heading, index) => {
const headingRect = heading.getBoundingClientRect();
if (headingRect.top <= viewportHeight / 2) {
if (currentActive) {
currentActive.classList.remove('active');
}
heading.classList.add('active');
currentActive = heading;
}
});
});
body {
margin: 0
}
section {
position: relative;
}
.sticky {
padding-bottom: 150px;
background: #2d232c;
position: sticky;
top: 0;
overflow: hidden;
height: auto;
color: white;
}
.animated-text {
opacity: 0;
height: 0;
overflow: hidden;
transition: opacity 1s ease, height 1s ease, transform 1s ease;
transform: translateY(0);
}
.animated-text.active {
height: auto;
opacity: 1;
transform: translateY(-20px);
}
.hero, .end {
height: 100px;
background: white;
}
<section class='hero'>
<p>Start</p>
</section>
<section class='sticky'>
<div class='text-animations'>
<h1 class='animated-text active'>Intro</h1>
<h2 class='animated-text'>First</h2>
<h2 class='animated-text'>Second</h2>
<h2 class='animated-text'>Third</h2>
</div>
<p class='intro_content'>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam
</p>
</section>
<section class='end'>
<p>End<p>
</section>
I am ended up using the translateY on scroll method better
const items = $('.animated-text')
const carousel = $('.sticky')
const windowHeight = $(window).height()
// Set the original data-offset value for each carousel item.
items.each(function (i) {
$(this).attr('data-offset', windowHeight * i)
})
$(window).scroll(function () {
const scrollTop = $(window).scrollTop()
const carouselTop = carousel.offset().top
const carouselBottom = carouselTop + carousel.height()
items.each(function () {
const itemIndex = $(this).index()
const itemOffset = parseInt($(this).attr('data-offset'), 10)
const nextItemOffset = items.eq(itemIndex + 1).attr('data-offset')
const offsetRatio = (scrollTop - itemOffset) / windowHeight
// Calculate opacity and transform values based on the scroll position.
let opacity = 1 - Math.abs(offsetRatio * 2)
const translateY = -offsetRatio * 100
// Only apply styles when the carousel is visible.
if (scrollTop >= carouselTop && scrollTop < carouselBottom) {
$(this).css({
opacity: opacity,
transform: `translateY(${translateY}%)`
})
// Check if the next item is visible, if so, hide the current item.
if (nextItemOffset && scrollTop >= nextItemOffset) {
$(this).css({ opacity: 0 })
}
} else if (scrollTop < carouselTop) {
// When scrolling is above the carousel, reset the transformation for all items
// But keep only the first item visible
if ($(this).index() === 0) {
$(this).css({
opacity: 1,
transform: 'translateY(0%)'
})
} else {
$(this).css({
opacity: 0,
transform: 'translateY(0%)'
})
}
}
})
})
body {
margin: 0
}
section {
position: relative;
}
.sticky {
height: 400vh;
}
.sticky__inner {
background: #2d232c;
overflow: hidden;
align-items: center;
color:#fff;
display:flex;
height: 100vh;
justify-content:center;
perspective:70rem;
position:sticky;
top: 0;
}
.text-animations {
height: 60vh;
position: relative;
transform-origin: 50% 50%;
transform-style: preserve-3d;
width: 100vw;
margin-top: -200px;
left: 70px;
}
.animated-text {
margin-top: 10vh; /* adjust this value as needed */
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
position: absolute;
transform-origin: 50% 50%;
transition: all 0.5s ease-in-out; /* Add this line */
opacity: 0;
}
.animated-text :first-of-type {
opacity: 1;
}
.hero, .end {
height: 100px;
background: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<section class='hero'>
<p>Start</p>
</section>
<section class='sticky'>
<div class='sticky__inner'>
<div class='text-animations'>
<h1 class='animated-text active'>Intro</h1>
<h2 class='animated-text'>First</h2>
<h2 class='animated-text'>Second</h2>
<h2 class='animated-text'>Third</h2>
</div>
<p class='intro_content'>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam
</p>
</div>
</section>
<section class='end'>
<p>End<p>
</section>