Here's what I'm trying to achieve:
-when the elements become visible to the user, the scroll-driven rotate animation starts on them one-by-one,and all 3 elements become sticky (meaning that they do not disappear while the user is scrolling down) until the animation on the last one is finished. -when the animation is finished, the page returns to its normal behavior
This code doesn't make the elements sticky when needed.
document.addEventListener('DOMContentLoaded', function() {
const elements = document.querySelectorAll('.animation');
let delay = 0;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.animationDelay = `${delay}s`;
entry.target.style.animationPlayState = 'running';
console.log('running');
// Add sticky class when the element is in view
entry.target.classList.add('sticky');
delay += 2.5; // Increase delay for the next element
observer.unobserve(entry.target); // Stop observing once the animation is triggered
}
});
}, {
threshold: 0.5
}); // Trigger when 50% of the element is in view
elements.forEach(element => {
console.log('paused');
element.style.animationPlayState = 'paused'; // Pause animation until triggered
observer.observe(element);
// Remove sticky class after the animation ends
element.addEventListener('animationend', () => {
console.log('animationend')
element.classList.remove('sticky');
});
});
});
.content {
width: 75%;
max-width: 800px;
margin: 0 auto;
}
p,
h1 {
font-family: Arial, Helvetica, sans-serif;
}
h1 {
font-size: 3rem;
}
p {
font-size: 1.5rem;
line-height: 1.5;
}
.animation {
view-timeline: --subjectReveal block;
animation-timeline: --subjectReveal;
width: 200px;
height: 200px;
background-color: bisque;
animation-iteration-count: 1;
animation-name: mymove;
animation-duration: 3s;
position: relative;
/* Normal position */
}
.animation:nth-child(1) {
animation-delay: calc(var(--subjectReveal) * 1);
}
.animation:nth-child(2) {
animation-delay: calc(var(--subjectReveal) * 2);
}
.animation:nth-child(3) {
animation-delay: calc(var(--subjectReveal) * 3);
}
/* Define the sticky behavior */
.sticky {
position: sticky;
top: 10px;
/* Adjust top as needed */
z-index: 10;
/* Ensure it stays on top */
}
@keyframes mymove {
0% {
opacity: 1;
transform: rotate(0deg);
}
50% {
opacity: 1;
transform: rotate(90deg);
}
100% {
opacity: 1;
transform: rotate(180deg);
}
}
.as-console-wrapper { height:50px; }
Scroll this
<div style="height:1000px; min-height: 300px; max-width: 100%; display: flex; flex-direction: row; justify-content: space-between; align-items: center;">
<div class="animation"></div>
<div class="animation"></div>
<div class="animation"></div>
</div>
Would you like to try this?
I used CSS animation-range-start
, It was in the code you provided..
but it seems difficult to support it on Firefox and Safari because it's an experimental function.
I've worked on the movements you want, so use the parts you can refer to!
I hope it helps :)
document.addEventListener('DOMContentLoaded', function() {
const elementsContent = document.querySelector('.animation-content');
const elements = document.querySelectorAll('.animation');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
elements.forEach((element) => {
element.style.animationName = 'mymove'; // add animation to div
element.style.animationPlayState = 'running'; // Start animation
});
observer.unobserve(entry.target); // Stop observing
}
});
}, {
threshold: 0.8
}); // Trigger when 80% of the element is in view
observer.observe(elementsContent);
});
html,
body {
margin: 0;
padding: 2vw;
background: #222;
}
.animation-wrap {
height: 300vh;
background: #fff;
}
.animation-content {
display: flex;
justify-content: space-around;
align-items: center;
height: 100vh;
position: sticky;
top: 0;
}
.animation {
view-timeline: --subjectReveal block;
animation-timeline: --subjectReveal;
animation-range-start: entry 200%;
height: 0; /* ignore this, just for square */
width: 20vw; /* ignore this, just for square */
padding-bottom:20vw; /* ignore this, just for square */
background-color: bisque;
animation-iteration-count: 1;
animation-duration: 3s;
animation-fill-mode: forwards;
/*whenendanimation,holdstartstate*/
animation-play-state: paused;
/*pausedatfirst*/
position: relative;
/*Normalposition*/
}
.animation:nth-child(1) {
animation-delay: 0s;
}
.animation:nth-child(2) {
animation-delay: 1.4s;
}
.animation:nth-child(3) {
animation-delay: 2.8s;
}
@keyframes mymove {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(360deg);
}
100% {
transform: rotate(360deg);
}
}
div[class*="other-content"] {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
font-size: 3rem;
}
.other-content_01 {
height: 400px;
background: #eee;
}
.other-content_02 {
height: 1000px;
background: #ddd;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="test.css">
</head>
<body>
<div class="other-content_01">
Scroll Page :)
</div>
<div class="animation-wrap">
<div class="animation-content">
<div class="animation"></div>
<div class="animation"></div>
<div class="animation"></div>
</div>
</div>
<div class="other-content_02">
Thank you!
</div>
<script src="test.js"></script>
</body>
</html>