https://codepen.io/BlazgoCompany/pen/dyBBRLp
我正在尝试制作一个带有大量滚动动画的网站(文本已被屏蔽)。第一个带有放大星星的动画效果很好,但第二个动画不起作用。它像平常一样播放动画,但是一旦完成,它就不会返回到常规滚动。动画完成后,它还应该继续其下方的文本。
我很确定我需要将其设置为相对但似乎无法让它工作。
另外,请不要建议使用
ScrollTrigger
,因为它不会永远保留在 CodePen 上,而且我不想获得 Club GSAP。
由于某种原因我无法使用堆栈片段,所以这里是完整的代码:
<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")
})
谢谢!
确保您的动画完成,并且
.gen-card
完成后重置。
使用这个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" });
}
});