<section class="demo">
<article class="demo__card"><span class="demo__index">01</span><h3>Easing</h3><p>속도는 감정을 만든다</p></article>
<article class="demo__card"><span class="demo__index">02</span><h3>Rhythm</h3><p>간격은 리듬을 만든다</p></article>
<article class="demo__card"><span class="demo__index">03</span><h3>Weight</h3><p>질량은 신뢰를 만든다</p></article>
<article class="demo__card"><span class="demo__index">04</span><h3>Restraint</h3><p>절제는 품격을 만든다</p></article>
<article class="demo__card"><span class="demo__index">05</span><h3>Timing</h3><p>타이밍은 의미를 만든다</p></article>
<article class="demo__card"><span class="demo__index">06</span><h3>Detail</h3><p>디테일은 완성을 만든다</p></article>
</section>.demo {
min-height: 240%;
padding: 18vh 24px;
display: flex;
flex-direction: column;
gap: 18px;
background: #08080a;
color: #f4f4f2;
font-family: "Helvetica Neue", Inter, system-ui, sans-serif;
}
.demo__card {
position: relative;
padding: 26px 24px;
border-radius: 18px;
border: 1px solid #1b1b20;
background: linear-gradient(160deg, #131317, #0c0c0f);
will-change: transform;
transform-origin: center center;
}
.demo__card h3 {
margin: 0 0 6px;
font-size: 20px;
font-weight: 500;
letter-spacing: -0.02em;
}
.demo__card p {
margin: 0;
font-size: 13px;
color: #77777e;
}
.demo__index {
position: absolute;
top: 22px;
right: 24px;
font-size: 11px;
letter-spacing: 0.18em;
color: #4d4d55;
}gsap.registerPlugin(ScrollTrigger);
const cards = gsap.utils.toArray(".demo__card");
// 속도값을 부드럽게 감쇠시키는 프록시
const skewSetter = gsap.quickSetter(cards, "skewY", "deg");
const scaleSetter = gsap.quickSetter(cards, "scaleY");
const clamp = gsap.utils.clamp(-14, 14);
const proxy = { skew: 0 };
ScrollTrigger.create({
onUpdate: (self) => {
const skew = clamp(self.getVelocity() / -180);
if (Math.abs(skew) > Math.abs(proxy.skew)) {
proxy.skew = skew;
gsap.to(proxy, {
skew: 0,
duration: 0.85,
ease: "power3.out",
overwrite: true,
onUpdate: () => {
skewSetter(proxy.skew);
scaleSetter(1 + Math.abs(proxy.skew) / 100);
},
});
}
},
});
// 등장은 별도로 — 속도 표현과 겹치지 않게 opacity만 사용합니다.
gsap.from(cards, {
opacity: 0,
duration: 0.8,
ease: "power2.out",
stagger: { each: 0.06 },
scrollTrigger: { trigger: ".demo", start: "top 90%" },
});