Это CSS.(Это я удачно зашёл, забрал в коллекцию css плюшек)
JS только для менюшки с настройками, ну и каждую букву он прячет в span тег - но этот код прямо на странице.
<html>
<head>
<meta charset="UTF-8">
<title>Текст Крутится</title>
<style>
@layer base, text, box;
@layer settings {
:root {
--radius: 27vmin;
--radius-text: calc(var(--radius) + 7vmin);
--blur: 8px;
--spinDuration: 25s;
--speedYMod: -3;
--speedXMod: 1;
--speedZMod: -1;
--perspective: 1200px;
}
body{perspective: var(--perspective);}
body,h1{transform-style: preserve-3d; }
}
@layer text {
h1 {
position: relative;
animation: spin3d var(--spinDuration) linear infinite;
span {
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%,-50%, 0) rotateY(calc(var(--i) * 360deg)) translateZ(var(--radius-text));
backface-visibility: visible;
}
}
@keyframes spin3d {
0% {transform: rotateX(0deg) rotateZ(0deg) rotateY(0deg);}
100% {transform:
rotateX(calc(360deg * var(--speedXMod)))
rotateZ(calc(360deg * var(--speedZMod)))
rotateY(calc(360deg * var(--speedYMod)));
}
}
}
@layer box {
dialog {
width: calc(2 * var(--radius));
backdrop-filter: blur(var(--blur));
transform: translateZ(0);
background: radial-gradient(var(--c-bg) 20%, rgb(from var(--c-bg) r g b / 0));
&:hover {
backdrop-filter: unset;
background-image: radial-gradient(
circle at 50% 50%,
transparent 0% 35%,
var(--c-bg) 35% 100%
);
background-repeat: repeat;
background-size: 3px 3px;
}
}
}
@layer base {
:root {
--c-black: #0a0a0a;
--c-white: #fff;
--s-line: 1px;
--c-line: var(--c-black);
--c-bg: var(--c-white);
font-family: sans-serif;
}
* {text-box: trim-both cap alphabetic;}
body {
display: grid;
place-content: center;
align-items: center;
min-height: 100dvh;
background: var(--c-bg);
color: var(--c-black);
& > * {grid-area: 1 / 1;}
}
h1 {
margin: 0;
padding: 0;
font-weight: 500;
font-size: calc(var(--radius) * 0.7);
pointer-events: none;
}
dialog {
position: relative;
grid-area: 1 / 1;
border: var(--s-line) solid var(--c-line);
padding: 0;
border-radius: 50%;
aspect-ratio: 1;
background: none;
}
}
</style>
</head>
<body>
<dialog open></dialog>
<h1 data-splittext>Текст Крутится</h1>
<script>
const $h1 = document.querySelector("[data-splittext]");
const letters = $h1.textContent.split("");
$h1.innerHTML = letters.map((c,i) =>`<span style="--i: ${i / (letters.length + 1)};">${c}</span>`).join("");
</script>
</body>
</html>