Ich habe den Code nach bestem Wissen und Gewissen zusammengestellt: https://codepen.io/Charles-Kent/pen/XJdZRWy
Code: Select all
import { gsap } from 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js';
import { ScrollTrigger } from 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/ScrollTrigger.min.js';
import { Lenis } from 'https://unpkg.com/[email protected]/dist/lenis.min.js';
document.addEventListener("DOMContentLoaded", () => {
gsap.registerPlugin(ScrollTrigger);
const lenis = new Lenis();
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);
const cardContainer = document.querySelector(".card-container");
const stickyHeader = document.querySelector(".sticky-header h1");
let isGapAnimationCompleted = false;
let isFlipAnimationCompleted = false;
function initAnimations() {
ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
const mm = gsap.matchMedia();
mm.add("(max-width: 999px)", () => {
document
.querySelectorAll(".card, .card-container, .sticky-header h1")
.forEach((el) => (el.style = ""));
return {};
});
mm.add("(min-width: 1000px)", () => {
ScrollTrigger.create({
trigger: ".sticky",
start: "top top",
end: `+=${window.innerHeight * 4}px`,
scrub: 1,
pin: true,
pinSpacing: true,
onUpdate: (self) => {
const progress = self.progress;
if (progress >= 0.1 && progress 0.25) {
gsap.set(stickyHeader, {
y: 0,
opacity: 1,
});
}
if (progress = 0.35 && !isGapAnimationCompleted) {
gsap.to(cardContainer, {
gap: "20px",
duration: 0.5,
ease: "power3.out",
});
gsap.to(["#card-1", "#card-2", "#card-3"], {
borderRadius: "20px",
duration: 0.5,
ease: "power3.out",
});
isGapAnimationCompleted = true;
} else if (progress < 0.35 && isGapAnimationCompleted) {
gsap.to(cardContainer, {
gap: "0px",
duration: 0.5,
ease: "power3.out",
});
gsap.to("#card-1", {
borderRadius: "20px 0 0 20px",
duration: 0.5,
ease: "power3.out",
});
gsap.to("#card-2", {
borderRadius: "0px",
duration: 0.5,
ease: "power3.out",
});
gsap.to("#card-3", {
borderRadius: "0 20px 20px 0",
duration: 0.5,
ease: "power3.out",
});
isGapAnimationCompleted = false;
}
if (progress >= 0.7 && !isFlipAnimationCompleted) {
gsap.to(".card", {
rotationY: 180,
duration: 0.75,
ease: "power3.inOut",
stagger: 0.1,
});
gsap.to(["#card-1", "#card-3"], {
y: 30,
rotationZ: (i) => [-15, 15][i],
duration: 0.75,
ease: "power3.inOut",
});
isFlipAnimationCompleted = true;
} else if (progress < 0.7 && isFlipAnimationCompleted) {
gsap.to(".card", {
rotationY: 0,
duration: 0.75,
ease: "power3.inOut",
stagger: -0.1,
});
gsap.to(["#card-1", "#card-3"], {
y: 0,
rotationZ: 0,
duration: 0.75,
ease: "power3.inOut",
});
isFlipAnimationCompleted = false;
}
},
});
return () => {};
});
}
initAnimations();
let resizeTimer;
window.addEventListener("resize", () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
initAnimations();
}, 250);
});
});Code: Select all
@import url("https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&display=swap");
:root {
--bg: #0f0f0f;
--fg: #fff;
--card-1: #b2b2b2;
--card-2: #ce2017;
--card-3: #2f2f2f;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Instrument Serif", sans-serif;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
h1 {
font-size: 4rem;
font-weight: 500;
line-height: 1;
}
p {
font-size: 2rem;
font-weight: 500;
line-height: 1;
}
section {
position: relative;
width: 100%;
height: 100svh;
padding: 2rem;
background-color: var(--bg);
color: var(--fg);
}
.intro,
.outro {
text-align: center;
align-content: center;
}
.intro h1,
.outro h1 {
width: 30%;
margin: 0 auto;
}
.sticky {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.sticky-header {
position: absolute;
top: 20%;
left: 50%;
transform: translate(-50%, -50%);
}
.sticky-header h1 {
position: relative;
text-align: center;
will-change: transform, opacity;
transform: translateY(40px);
opacity: 0;
}
.card-container {
position: relative;
width: 75%;
display: flex;
perspective: 1000px;
transform: translateY(40px);
will-change: width;
}
.card {
position: relative;
flex: 1;
aspect-ratio: 5/7;
transform-style: preserve-3d;
transform-origin: top;
}
#card-1 {
border-radius: 20px 0 0 20px;
}
#card-3 {
border-radius: 0 20px 20px 0;
}
.card-front,
.card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: inherit;
overflow: hidden;
}
.card-back {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
transform: rotateY(180deg);
padding: 2rem;
}
.card-back span {
position: absolute;
top: 2rem;
left: 2rem;
opacity: 0.4;
}
#card-1 .card-back {
background-color: var(--card-1);
color: var(--bg);
}
#card-2 .card-back {
background-color: var(--card-2);
}
#card-3 .card-back {
background-color: var(--card-3);
}
@media (max-width: 1000px) {
h1 {
font-size: 3rem;
}
.intro h1,
.outro h1 {
width: 100%;
}
.sticky {
height: max-content;
padding: 4rem 2rem;
flex-direction: column;
}
.sticky-header {
position: relative;
top: 0;
left: 0;
transform: none;
margin-bottom: 4rem;
}
.sticky-header h1 {
opacity: 1;
}
.card-container {
width: 100%;
flex-direction: column;
gap: 2rem;
}
.card {
width: 100%;
max-width: 400px;
margin: 0 auto;
border-radius: 20px !important;
}
#card-1,
#card-2,
#card-3,
.card-back {
transform: none;
}
}Code: Select all
RedoMedia Split-Card Scroll Animation | Codegrid
Every idea begins as a single image
Three pillars with one purpose
[img]https://www.charlesthedesigner.com/images/card-images/card_cover_1.jpg[/img]
( 01 )
Interactive Web Experiences
[img]https://www.charlesthedesigner.com/images/card-images/card_cover_2.jpg[/img]
( 02 )
Thoughtful Design Language
[img]https://www.charlesthedesigner.com/images/card-images/card_cover_3.jpg[/img]
( 03 )
Visual Design Systems
Every transition leaves a trace
Dies basiert auf einem CodeGrid-Tutorial, das ich auf YouTube gefunden habe (das zeigt, wie das Endergebnis aussehen sollte):
Sieht dort relativ einfach aus, lässt sich für mich aber in keiner Weise ausführen ... nur mein Glück, oder? Wo geht bei mir der Code schief? Nochmals vielen Dank.
Mobile version