<section class="demo">
<div class="demo__scene">
<div class="demo__ring" id="ring" data-text="DESIGNBASE · INTERACTION · CODE · "></div>
</div>
</section>.demo {
width: 100%;
min-height: 100dvh;
display: grid;
place-items: center;
background: radial-gradient(120% 80% at 50% 50%, #131318 0%, #08080a 70%);
color: #f4f4f2;
font-family: "Helvetica Neue", Inter, system-ui, sans-serif;
overflow: hidden;
}
.demo__scene {
perspective: 900px;
}
.demo__ring {
position: relative;
width: 260px;
height: 60px;
transform-style: preserve-3d;
will-change: transform;
}
.demo__ring span {
position: absolute;
top: 0;
left: 50%;
height: 60px;
line-height: 60px;
font-size: 30px;
font-weight: 500;
letter-spacing: 0;
transform-origin: 50% 50%;
backface-visibility: hidden;
}const ring = document.querySelector("#ring");
const chars = ring.dataset.text.split("");
const step = 360 / chars.length;
const radius = 150;
ring.innerHTML = chars.map((char) => "<span>" + char + "</span>").join("");
// 각 글자를 원통 둘레에 세웁니다.
gsap.utils.toArray(".demo__ring span").forEach((span, index) => {
gsap.set(span, {
rotationY: index * step,
transformOrigin: "50% 50% " + -radius + "px",
xPercent: -50,
});
});
// 살짝 기울인 축에서 등속 회전 — 원근 때문에 속도가 있어 보입니다.
gsap.set(ring, { rotationX: -12 });
gsap.to(ring, { rotationY: 360, duration: 18, ease: "none", repeat: -1 });
gsap.from(".demo__ring span", { opacity: 0, duration: 1, ease: "power2.out", stagger: 0.02 });