DEMO
SCROLL DOWN · EXPLORE MORE ·
CODE
<div class="demo-stage spt-stage">
<div class="spt-wrap">
<p class="spt-sr">SCROLL DOWN · EXPLORE MORE ·</p>
<div class="spt-ring" id="spt-ring" aria-hidden="true" data-text="SCROLL DOWN · EXPLORE MORE · "></div>
<span class="spt-center" aria-hidden="true">↓</span>
</div>
</div>
.spt-stage { display:flex; align-items:center; justify-content:center; padding:2rem; background:#0B0B10; min-height:280px; }
.spt-wrap { position:relative; width:180px; height:180px; }
.spt-sr { position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
.spt-ring { position:absolute; inset:0; animation: spt-spin 14s linear infinite; }
.spt-ch { position:absolute; left:50%; top:0; height:50%; width:1em; margin-left:-.5em; text-align:center; transform-origin:bottom center;
font-family:'Oswald',sans-serif; font-size:.95rem; letter-spacing:0; color:#EDEDED; }
.spt-center { position:absolute; top:50%; left:50%; width:56px; height:56px; margin:-28px 0 0 -28px; border-radius:50%;
display:flex; align-items:center; justify-content:center; background:#E91B89; color:#fff; font-size:1.3rem; }
.spt-wrap:hover .spt-ring { animation-play-state:paused; }
@keyframes spt-spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
.spt-ring { animation:none; }
}
(function(){
var ring = document.getElementById('spt-ring');
if (!ring) return;
var chars = Array.from(ring.dataset.text);
var step = 360 / chars.length;
chars.forEach(function(c, i){
var s = document.createElement('span');
s.className = 'spt-ch';
s.textContent = c;
s.style.transform = 'rotate(' + (i * step) + 'deg)';
ring.appendChild(s);
});
})();
AI PROMPT
文字を円周に沿って並べ、ゆっくり回転させる Spinning Text(サークルテキスト)を作って。一辺180pxの正方形の入れ物の中に、文字列を1文字ずつ span にして置く。各 span は left:50%・top:0・高さを入れ物の半分(50%)にして transform-origin: bottom center とし、transform: rotate(i × 360 / 文字数 deg) で円周上に配置する(中心から半径ぶん上に置いて回すのが要点)。文字の分割は split('') ではなく Array.from で行い、😀 のような絵文字が2つに割れないようにする(👨👩👧 のように複数を組み合わせた絵文字まで1文字として扱う場合は Intl.Segmenter の granularity: 'grapheme' を使う)。末尾に区切り(「 · 」と空白)を入れて、1周したときに先頭とつながるようにする。全体を @keyframes で rotate(360deg) まで14秒 linear の無限ループで回し、ホバーで animation-play-state: paused にする。中央には円形のアイコンを置く。読み上げ用に同じ文を視覚的に隠した要素で持たせ、回転する側は aria-hidden="true" にする。@media (prefers-reduced-motion: reduce) では回転を止める。
Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
文字を円周に沿って並べ、全体をゆっくり回転させる演出です。Magic UI にも同じ名前のコンポーネント(Spinning Text)があり、ポートフォリオサイトの「SCROLL DOWN」表示や、ブランド名を丸いスタンプのように見せる装飾によく使われます。
当サイトの Orbiting Circles は小さな円が軌道上を回る演出、Rotating Words は単語を上下に入れ替える演出、Offset Path Motion は要素を任意の曲線に沿って動かす演出です。本ページは文字そのものを円形に組む別テーマで、SVG の textPath を使わずに span の回転だけで作れます。
回転は目を引く反面、読むには不向きです。短い決まり文句や装飾的な一文に使い、重要な情報は読み上げ用の要素と通常のテキストで別に示してください。
文字数が多い・少ないとどうなりますか?
角度は「360 ÷ 文字数」で自動計算するため、文字数が変わっても1周にぴったり収まります。ただし文字数が多すぎると文字同士が重なり、少なすぎると間隔が空きます。フォントサイズと入れ物の大きさに対して、円周の長さ(直径 × 3.14)に収まる文字数を目安にしてください。
Array.from で分割するのはなぜですか?
文字列を split('') で分けると、😀 のような絵文字が2つの壊れた文字に分かれます(内部で2つの単位に分かれて保存されているため)。Array.from はコードポイント単位で分割するので、こうした絵文字も1文字として配置できます。ただし 👨👩👧 のように複数の絵文字を組み合わせたものは Array.from でも分かれるため、見た目の1文字単位で扱うには Intl.Segmenter(granularity: 'grapheme')を使ってください。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。