Я собрал код в меру своих возможностей: https://codepen.io/Charles-Kent/pen/XJdZRWy
Код: Выделить всё
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/lenis@1.1.5/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);
});
});Код: Выделить всё
@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;
}
}Код: Выделить всё
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
Это основано на учебнике CodeGrid, который я нашел на YouTube (который демонстрирует, как должен выглядеть конечный результат):
Выглядит относительно просто, но у меня никак не получается... просто мне повезло, верно? Где у меня код идет не так? Еще раз спасибо.
Подробнее здесь: https://stackoverflow.com/questions/798 ... to-execute
Мобильная версия