https://codepen.io/BlazgoCompany/pen/dyBBRLp
I am trying to make a website with lots of scroll animations (the text has been masked). The first animation with the zooming in star works very well, but the second animation doesn't work. It plays the animation like normal, but once it is done, it doesn't go back to the regular scrolling. It should also continue with the text below it after the animation is done.
I am pretty sure I need to set it to relative but can't seem to get it to work.
Also, please do not suggest using ScrollTrigger
because it will not stay on CodePen forever, and I do not want to get Club GSAP.
I am unable to use Stack Snippets for some reason so here's the full code:
<div class="top-right">
<span class="cta-login">
<button class="my-dash btn-base">Lorem Ipsum</button>
</span>
</div>
<div class="nav">
<span class="logo">
<!-- Logo Placeholder -->
</span>
<span class="cta-login">
<button class="login btn-base" onclick="openModal()">Login</button>
</span>
</div>
<div class="hero">
<h1 class="hero-text">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h1>
</div>
<div class="content">
<small class="subtitle">Sed do eiusmod tempor incididunt.</small>
<h2 style="margin-bottom: 0">
<span class="brand gradient teal text">Lorem.</span>
<span class="brand gradient blue text">Ipsum.</span>
</h2>
<h2 class="brand gradient red text">Dolor Sit Amet.</h2>
<p style="color: #999">Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. <span class="highlight">Ut enim</span> ad minim veniam, quis nostrud <span class="highlight">exercitation</span> ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
<button class="getstarted">Get Started</button>
<div class="spacer-240"></div>
<h1 class="tw-heading gray">Placeholder Heading</h1>
<div class="gen-card">
<h2>Placeholder Title</h2>
<h4>Placeholder Subtitle</h4>
<p>Placeholder text for description.</p>
</div>
<div class="gen-card">
<h2>Placeholder Title</h2>
<h4>Placeholder Subtitle</h4>
<p>Placeholder text for description.</p>
</div>
<div class="gen-card">
<h2>Placeholder Title</h2>
<h4>Placeholder Subtitle</h4>
<p>Placeholder text for description.</p>
</div>
<div class="gen-card">
<h2>Placeholder Title</h2>
<h4 class="anim brand red text">Placeholder Subtitle</h4>
<p>Placeholder text for description.</p>
</div>
This text should appear after this scroll animation (but it doesn't!)
</div>
@import url('https://fonts.googleapis.com/css?family=Nunito:200,200i');
@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500');
@import url('https://fonts.googleapis.com/css?family=Varela+Round');
/* Import fonts */
/* Global Styles */
body {
background: black;
overflow-x: hidden;
min-height: 800vh;
margin: 0;
font-family: sans-serif;
/* Fallback font */
}
input {
padding: 12px;
width: 80%;
border-radius: 8px;
border: none;
left: 50%;
position: relative;
transform: translate(-50%);
margin-top: 42px;
}
.hero {
padding: 16px;
position: fixed;
width: 100%;
height: 100vh;
background: radial-gradient(circle at 0% -10%, var(--gradient-color), transparent 38%);
}
.hero-text {
color: white !important;
font-size: 64px;
font-family: "Varela Round", sans-serif;
/* Default to Varela Round, fallback to sans-serif */
display: inline-block;
position: relative;
z-index: 10;
}
.hero-text svg {
transform: translateY(-4px);
display: inline;
vertical-align: middle;
background-color: transparent;
}
.content {
color: white;
display: none;
font-family: "Varela Round";
text-align: center;
}
.highlight {
color: #e3b505;
}
.btn-base,
.modal .login,
.backbtn {
border-radius: 20px;
border: none;
padding: 0 12px;
height: 30px;
background: #e3b505;
font-size: 16px;
font-weight: bold;
}
.btn-base:hover,
.modal .login:hover,
.backbtn:hover {
background: #c8a104;
}
.getstarted {
padding: 8px 16px;
border-radius: 32px;
height: auto;
}
.nav {
position: fixed;
top: -36px;
background: radial-gradient(circle at 0% center, #066ab2, transparent 80%);
backdrop-filter: blur(8px);
width: calc(100% - 32px);
height: 36px;
z-index: 1000;
left: 50%;
transform: translate(-50%, 0);
max-width: 500px;
border-radius: 32px;
}
button:active {
transform: scale(0.9)
}
button {
transition: transform 0.1s;
}
.btncontainer {
margin-top: 48px;
left: 50%;
position: relative;
transform: translate(-50%);
display: inline-block;
}
.btn-close {
position: absolute;
right: 8px;
top: 8px;
width: 32px;
height: 32px;
background: #95190C;
color: white
}
.btn-close:hover {
background: #711409;
}
.loader {
height: 24px;
width: 24px;
border: 3px solid transparent;
border-bottom: 3px solid black;
border-radius: 32px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.login-modal h1 {
font-size: 24px;
font-weight: bold;
text-align: center;
margin-bottom: -20
}
.brand.gradient.teal.text {
background: #107E7D;
background: -webkit-linear-gradient(#28E2DF, #0C5A59);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.brand.gradient.blue.text {
background: #066AB1;
background: -webkit-linear-gradient(#89CBFB, #05528A);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.brand.gradient.blue.extra {
background: #066AB1;
background: -webkit-linear-gradient(#C4E5FD, #05528A);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.brand.gradient.red.text {
margin-top: 0;
background: #971A0C;
background: -webkit-linear-gradient(#F6877B, #711409);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
h2 {
font-size: 32px;
}
.subtitle {
font-size: 14px;
background: -webkit-linear-gradient(#eee, #3f3f3f);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle::after,
.subtitle::before {
content: '';
width: 64px;
height: 1px;
display: inline-block;
}
.subtitle::before {
transform: translate(-8px, -4px);
background: linear-gradient(-90deg, #aaa 0%, transparent 100%);
right: 120%;
top: 50%;
}
.subtitle::after {
transform: translate(8px, -4px);
background: linear-gradient(90deg, #aaa 0%, transparent 100%);
left: 120%;
top: 50%;
}
h1.gray.tw-heading {
font-size: 48px;
background: -webkit-linear-gradient(#eee, #333);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.spacer-240 {
height: 240px;
}
.gen-card {
width: calc(25% - 14px);
margin: 0 4px;
display: inline-block;
height: 360;
background: transparent;
backdrop-filter: blur(4px);
opacity: 0;
border-radius: 16px;
text-align: center;
padding: 12px;
border: 1px solid #555
}
.gen-card h2 {
margin: 0;
}
.fixed {
position: fixed;
}
.relative{
position: relative;
}
/* .anim.brand.red.text{
margin-top: 0;
background: #971A0C;
background: -webkit-linear-gradient(#F6877B, #711409);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
} */
function setTransformOriginToChild() {
const parent = document.querySelector('.hero-text');
const child = document.querySelector('.hero-text .star');
const parentRect = parent.getBoundingClientRect();
const childRect = child.getBoundingClientRect();
const childCenterX = (childRect.left - 10 + childRect.right) / 2;
const childCenterY = (childRect.top + 4 + childRect.bottom) / 2;
const transformOriginX = (childCenterX - parentRect.left) / parentRect.width * 100;
const transformOriginY = (childCenterY - parentRect.top) / parentRect.height * 100;
parent.style.transformOrigin = `${transformOriginX}% ${transformOriginY}%`;
}
window.addEventListener('resize', setTransformOriginToChild);
// Function to split text into multiple spans
function splitTextIntoSpans() {
// Get the container element
const container = document.querySelector('.hero-text');
// Get the text content of the container
const text = container.textContent;
// Create a document fragment to hold the new span elements
const fragment = document.createDocumentFragment();
// Define words to style
const wordsToStyle = {
'future': '#066AB2',
'sit': '#066AB2'
};
// Helper function to create span elements for text
function createSpansForText(text, color = '') {
const fragment = document.createDocumentFragment();
for (let char of text) {
const span = document.createElement('span');
span.textContent = char;
if (color) {
span.classList.add("brand")
span.classList.add("gradient")
span.classList.add("blue")
span.classList.add("text")
span.classList.add("extra")
}
fragment.appendChild(span);
}
return fragment;
}
// Process the text letter by letter
let index = 0;
while (index < text.length) {
// Find the next word or space
let nextSpace = text.indexOf(' ', index);
if (nextSpace === -1) {
nextSpace = text.length;
}
var word = text.slice(index, nextSpace);
// Add a space character after the word
if (nextSpace < text.length) {
word += ' ';
}
// Create spans for each letter of the word
const color = wordsToStyle[word.trim()] || '';
const wordFragment = createSpansForText(word, color);
fragment.appendChild(wordFragment);
// If the word add a special span element after it
if (word.trim() === 'sit') {
const starSpan = document.createElement('span');
starSpan.className = 'star';
starSpan.innerHTML = `
<svg width="32" height="24">
<path d="M 2 12 q 10 0 10 -10 q 0 10 10 10 q -10 0 -10 10 q 0 -10 -10 -10 M 0 12 A 1 1 0 0 0 24 12 A 1 1 0 0 0 0 12"></path>
</svg>`;
fragment.appendChild(starSpan);
}
// Move to the next word
index = nextSpace + 1;
}
// Clear the original container
container.textContent = '';
// Append the fragment to the container
container.appendChild(fragment);
}
function calculateTravelDistance() {
// Get the .star element
const starElement = document.querySelector('.hero-text .star');
// Get the bounding rectangle of the .star element
const starRect = starElement.getBoundingClientRect();
// Get the center of the .star element
const starCenterX = starRect.left + starRect.width / 2;
const starCenterY = starRect.top + starRect.height / 2;
// Get the center of the screen
const screenCenterX = window.innerWidth / 2;
const screenCenterY = window.innerHeight / 2;
// Calculate the amount to move the element
const moveX = screenCenterX - starCenterX;
const moveY = screenCenterY - starCenterY;
return { moveX, moveY };
}
// Call the function to split the text
window.addEventListener('load', () => {
splitTextIntoSpans();
setTransformOriginToChild()
tl = gsap.timeline({
})
tl.set(".hero-text span", { filter: "blur(32px)" })
tl.set(".hero", { "--gradient-color": "#FFD700" })
tl.set(".hero-text svg", { fill: "white" })
tl.to(".hero-text span", { filter: "blur(0px)", duration: 0.5, stagger: 0.05 })
tl.to(".top-right", { opacity: 0, duration: 0.8 }, "<+=1.2")
tl.to(".hero-text", { y: calculateTravelDistance().moveY, x: calculateTravelDistance().moveX, duration: 3, fill: "black", ease: "power4.in" }, "+=0.5")
tl.to(".hero-text", { scale: 90, duration: 3, ease: "power4.in" }, "<")
tl.to(".hero-text svg", { duration: 1, fill: "black", background: "transparent" }, "-=1")
tl.to(".hero", { duration: 1, "--gradient-color": "#000" }, "-=1")
tl.to(".nav", { duration: 1, top: "16px" }, "-=1")
tl.pause(0.5)
});
var gentlmarker = 1000000
var gentl = 0
document.addEventListener("scroll", () => {
//get scroll position
const scrollPosition = window.scrollY;
if ((scrollPosition / 200) + 0.5 < 5.8) {
gsap.set(".hero", { display: "block" })
gsap.set(".content", { display: "none" })
tl.pause((scrollPosition / 200) + 0.5)
}
else {
gsap.set(".hero", { display: "block" })
gsap.set(".content", { display: "none" })
tl.pause((scrollPosition / 200) + 0.5)
gsap.set(".hero", { display: "none" })
gsap.set(".content", { position: "relative", top: 1060 + window.innerHeight, display: "block" })
}
if(document.querySelector(".tw-heading").getBoundingClientRect().top < 200 && scrollPosition > 1800){
//"pin" the tw-heading and the gencards for 300 px of scrolling
gsap.set(".tw-heading", {position: "fixed", top: 200-32, left: "50%", xPercent: -50, width: "100%"})
gc = document.querySelectorAll(".gen-card")
gcw = gc[0].getBoundingClientRect().width
gsap.set(".gen-card", {position: "fixed", opacity: 1, top: 264, right: -gcw})
gentl=gsap.timeline({})
// gentl.to(gc[0], {position: "fixed", top: 264, left: 8})
// gentl.to(gc[1], {position: "fixed", top: 264, left: 8+gcw+8})
// gentl.to(gc[2], {position: "fixed", top: 264, left: 8+gcw+8+gcw+8})
// gentl.to(gc[3], {position: "fixed", top: 264, left: 8+gcw+8+gcw+8+gcw+8})
// Common properties for the animation
const commonProps = {
position: "fixed",
top: 264
};
// Define the start position and end positions for gc elements
const startPosition = -200;
const endPositionFirst = -gcw;
const endPositionLast = 32;
// Create a timeline animation with stagger
gentl.to(gc, {
...commonProps,
left: endPositionFirst,
stagger: 0.2, // Stagger by 0.1 seconds for each element
duration: 1, // Adjust duration to your preference
ease: "power1.inOut" // Example easing function
})
.to(gc[gc.length - 1], {
left: endPositionLast,
duration: 1, // Same duration as above to keep consistency
ease: "power1.inOut" // Use the same easing function
}, "-=1")
.to(gc[3], {
width: "50%",
left: "50%",
x: "-50%",
duration: 1, // Same duration as above to keep consistency
ease: "power1.inOut"
})
.addLabel("fgenfocus")
.to(gc[3].querySelector("h2"), {
opacity: 0
}, "fgenfocus")
.to(gc[3].querySelector("h2"), {
opacity: 0,
height: "0px",
margin: 0
}, "fgenfocus")
.add(function(){
gc[3].querySelector("h4").innerHTML="Now the th scrolling should continue as normal"
}, "fgenfocus")
.to(gc[3].querySelector("h4"), {
fontSize: "32px",
margin: 0
}, "fgenfocus")
// gentl.to([gc[0], gc[1], gc[2]], {width: 0})
gentl.pause()
gentlmarker = scrollPosition
}
if(scrollPosition>gentlmarker){
gc = document.querySelectorAll(".gen-card")
gcw = gc[0].getBoundingClientRect().width
console.log(gentlmarker)
console.log((scrollPosition-gentlmarker)/300)
console.log("----------------")
gentl.pause((scrollPosition-gentlmarker)/300)
}
if((scrollPosition-gentlmarker)/300 < 0){
gsap.set(".tw-heading", {position: "static", xPercent: 0})
gsap.set(".gen-card", { opacity: 0 })
}
})
document.querySelector(".getstarted").addEventListener("click", () => {
let clickTl = gsap.timeline({})
clickTl.to(".getstarted", { scale: 100, duration: 2, ease: "power4.in" })
clickTl.to(".getstarted", { background: "black", color: "black", duration: 1, ease: "power4.in" }, "-=1")
})
Thanks!
Make sure that your animation finishes and that .gen-card
reset after they're done.
Use this Javascript:
document.addEventListener("scroll", () => {
const scrollPosition = window.scrollY;
if (scrollPosition / 200 + 0.5 < 5.8) {
gsap.set(".hero", { display: "block" });
gsap.set(".content", { display: "none" });
tl.pause(scrollPosition / 200 + 0.5);
} else {
gsap.set(".hero", { display: "none" });
gsap.set(".content", { display: "block", top: 1060 + window.innerHeight });
}
const twHeadingTop = document.querySelector(".tw-heading").getBoundingClientRect().top;
// Trigger the second animation
if (twHeadingTop < 200 && scrollPosition > 1800) {
gsap.set(".tw-heading", {
position: "fixed",
top: 200 - 32,
left: "50%",
xPercent: -50,
});
const gc = document.querySelectorAll(".gen-card");
const gcw = gc[0].getBoundingClientRect().width;
gsap.set(gc, {
position: "fixed",
opacity: 1,
top: 264,
right: -gcw,
});
gentl = gsap.timeline({})
.to(gc, {
position: "fixed",
top: 264,
left: -gcw,
stagger: 0.2,
duration: 1,
ease: "power1.inOut",
})
.to(gc[gc.length - 1], {
left: 32,
duration: 1,
ease: "power1.inOut",
}, "-=1")
.to(gc[3], {
width: "50%",
left: "50%",
x: "-50%",
duration: 1,
ease: "power1.inOut",
})
.addLabel("fgenfocus")
.to(gc[3].querySelector("h2"), {
opacity: 0,
}, "fgenfocus")
.to(gc[3].querySelector("h2"), {
opacity: 0,
height: "0px",
margin: 0,
}, "fgenfocus")
.add(() => {
gc[3].querySelector("h4").innerHTML = "Now the scrolling should continue as normal";
}, "fgenfocus")
.to(gc[3].querySelector("h4"), {
fontSize: "32px",
margin: 0,
}, "fgenfocus");
gentl.pause();
gentlmarker = scrollPosition;
}
if (scrollPosition > gentlmarker) {
const gc = document.querySelectorAll(".gen-card");
gcw = gc[0].getBoundingClientRect().width;
gentl.pause((scrollPosition - gentlmarker) / 300);
}
if ((scrollPosition - gentlmarker) / 300 < 0) {
gsap.set(".tw-heading", { position: "static", xPercent: 0 });
gsap.set(".gen-card", { position: "static", opacity: 1 });
}
if (scrollPosition > gentlmarker + 300) {
gsap.set(".tw-heading", { position: "relative" });
gsap.set(".gen-card", { position: "relative", top: "auto" });
}
});