ローディングのスピナーやアイコンの装飾など、画像を回転させ続ける(くるくる回す)表現はよく使います。実現方法はCSSとJavaScriptの2通りありますが、基本はCSSが正解です。理由もあわせて見ていきましょう。
animation: spin Ns linear infinite が最もシンプルで滑らか(GPU合成で軽い)です。スクロール量やユーザー操作に連動させたいときだけJavaScriptを使い、その場合は setInterval ではなく requestAnimationFrame + 経過時間で実装します。CSSで回転させ続ける(推奨)
@keyframes で 0deg→360deg の回転を定義し、animation に infinite(無限ループ)と linear(等速)を指定します。これだけで回り続けます。
.rotate-image {
animation: spin 2s linear infinite; /* 2秒で1回転・等速・無限 */
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
animation-duration で調整します。2s より 1s のほうが速く、5s にするとゆっくり回ります(1回転にかかる時間)。回転の中心をずらしたいときは transform-origin を指定します(既定は中心)。滑らかに回すコツ(GPU合成)
transform による回転はGPUで合成されるため、レイアウトの再計算が起きず軽快です。さらに will-change: transform を添えると、ブラウザに「この要素は変化する」と事前に伝えられます。
.rotate-image {
animation: spin 2s linear infinite;
will-change: transform; /* 合成の最適化ヒント(多用は避ける) */
}
will-change は常時動く要素に限って使います。ページ中の多くの要素に付けるとメモリを消費し逆効果です。top/left で動かすと再レイアウトが走り重くなるため、回転・移動はtransform で行うのが鉄則です。動きを抑える設定に配慮する(prefers-reduced-motion)
無限に回り続けるアニメーションは、めまいや乗り物酔いを起こしやすいユーザーに負担になります。OSの「視差効果を減らす/動きを減らす」設定を尊重するため、@media (prefers-reduced-motion: reduce) でアニメーションを止めるのがアクセシビリティの基本です。
@media (prefers-reduced-motion: reduce) {
.rotate-image {
animation: none; /* 回転を止める */
}
}
マウスを乗せたら一時停止する
animation-play-state を切り替えるだけで、アニメーションをその場で一時停止・再開できます。JavaScriptは不要です。
.rotate-image:hover {
animation-play-state: paused; /* マウスオーバー中だけ止まる */
}
JavaScriptで制御する(requestAnimationFrame)
スクロール量やボタン操作と連動させたいときはJavaScriptを使います。ただし setInterval は使いません。requestAnimationFrame なら画面の更新タイミングに同期して滑らかで、背景タブでは自動的に止まる(電池に優しい)からです。
ポイントは「経過時間(delta time)で角度を進める」こと。フレームごとに固定値を足すと、端末のフレームレートで速度が変わってしまいます。経過秒数を掛ければどの環境でも同じ速度になります。
const el = document.querySelector(".rotate-image");
const degPerSec = 180; // 1秒あたり180度回す
let angle = 0;
let last = null;
let rafId;
function tick(now) {
if (last !== null) {
const dt = (now - last) / 1000; // 経過秒数
angle = (angle + degPerSec * dt) % 360;
el.style.transform = `rotate(${angle}deg)`;
}
last = now;
rafId = requestAnimationFrame(tick);
}
rafId = requestAnimationFrame(tick);
// 停止したいとき
// cancelAnimationFrame(rafId);
setInterval(fn, 50) は約20fpsでカクつき、固定値を足す方式は更新間隔で速度が変わって不安定です(同じ「2度ずつ」でも50ms間隔なら1秒で40度、25ms間隔なら80度になる)。requestAnimationFrame + 経過時間なら速度が一定に保たれます。逆回転・3D風の回転にする
回転方向や軸を変えるのも簡単です。逆回転は終了角度をマイナスに、立体的な反転は rotateY を使います。
/* 逆回転(反時計回り) */
@keyframes spin-reverse {
from { transform: rotate(0deg); }
to { transform: rotate(-360deg); }
}
/* Y軸でコインのように回す */
@keyframes flip-y {
from { transform: rotateY(0deg); }
to { transform: rotateY(360deg); }
}
CSS と JavaScript の使い分け
| 状況 | おすすめ |
|---|---|
| ただ回し続ける/ローディング表示 | CSS(animation infinite) |
| 速度を一定に保ちたい | CSS、またはJSならrAF+経過時間 |
| スクロール量・操作に連動 | JavaScript(requestAnimationFrame) |
| hoverで一時停止 | CSS(animation-play-state) |
よくある質問(FAQ)
animation: spin 2s linear infinite; を指定し、@keyframes spin で 0deg→360deg を定義する方法です。JavaScript不要で滑らかに回り続けます。animation-duration を変えます。値が小さいほど速く、大きいほどゆっくり回ります(1回転にかかる時間)。JavaScriptなら回転量を「1秒あたり何度」で持ち、経過時間(delta time)を掛けて加算すると速度が安定します。setInterval は画面の更新タイミングと無関係に動くためフレーム落ちしやすく、背景タブでも回り続けます。requestAnimationFrame は描画に同期して滑らかで、非表示タブでは自動的に止まるため省電力です。@media (prefers-reduced-motion: reduce) で animation: none; を指定し、OSの「動きを減らす」設定を尊重しましょう。まとめ
画像を回転させ続ける方法のポイントを整理します。
- 基本は CSS
animation: spin Ns linear infinite(軽くて滑らか) - 速度は
animation-duration、中心はtransform-origin - 回転・移動は
transformで(top/leftは重い) prefers-reduced-motionで停止してアクセシビリティに配慮- JS連動は
requestAnimationFrame+ 経過時間(setIntervalは使わない)
関連として、同じ requestAnimationFrame を使う数字をカウントアップアニメーションさせる方法・ページを自動スクロールさせる方法もあわせて読むと、滑らかなアニメーション制御に強くなれます。
