1. ホーム
  2. CSS

【CSS】@keyframes と animation で繰り返しアニメーションを作る方法

Share

ローディング中にくるくる回るアイコンや、ページを開いた瞬間にふわっと現れる文字。こうした「きっかけなしで自律的に動き続けるアニメーション」は、CSS の @keyframesanimation プロパティで作れます。この記事では、@keyframes でアニメーションの動きを定義し、animation で要素に適用する基本から、各サブプロパティの役割、繰り返しや往復の指定、そして「動かない・1 回で止まる」というつまずきの原因までを、初心者の方にも分かるように解説します。回転スピナーやフェードインの動くデモも用意しました。

@keyframes と animation でできること

CSS でアニメーションを付ける方法は大きく 2 つあります。1 つは transition、もう 1 つがこの記事で扱う @keyframesanimation です。@keyframes は「アニメーションの途中経過(キーフレーム)」を定義するルールで、animation はそのキーフレームを要素に当てはめて再生するためのプロパティです。役割が分かれているのがポイントで、まず動きの中身を @keyframes で作り、それを animation で呼び出して使います。

これにより、ホバーやクリックといったきっかけがなくても、ページを表示しただけで要素を動かしたり、同じ動きを何度も繰り返したりできます。回転し続けるローディングスピナー、点滅するアイコン、ゆっくり拡大と縮小を繰り返すボタンなどは、すべて @keyframesanimation の組み合わせで実現できます。

transition との違い

混同しやすいのが transition との違いです。transition は、要素のプロパティが ある値から別の値へ変わるとき、その変化をなめらかにつなぐものです。そのため :hover やクラスの付け外しといった「値を変えるきっかけ」が必ず必要で、基本は始まりと終わりの 2 状態を行き来するだけです。

一方 animation は、@keyframes に書いた途中経過に沿って 自律的に動きます。ページを開いた瞬間に再生を始められ、回数を指定して繰り返したり無限ループさせたりできます。さらに、中間地点を複数定義して複雑な動きを作れるのも transition にはない特徴です。ざっくり言えば、ホバーで色をふわっと変える程度なら transition、回り続ける・点滅し続けるといった繰り返しの動きなら animation が向いています。transition 自体の詳しい使い方は別記事で解説しているので、ここでは違いの整理に留めます。

@keyframes の書き方

@keyframes は、アニメーションの名前と、再生の各時点でのスタイルを定義します。もっとも単純なのは、開始時点を from、終了時点を to で書く形です。次の例は、要素を 0 度から 360 度まで回転させる動きを spin という名前で定義しています。

style.css
/* spin という名前でアニメーションを定義 */
@keyframes spin {
  from {
    transform: rotate(0deg);   /* 開始時点の状態 */
  }
  to {
    transform: rotate(360deg); /* 終了時点の状態 */
  }
}

from は再生の 0%、to は 100% を表します。途中の動きをもっと細かく制御したいときは、from / to の代わりに 0% から 100% までのパーセンテージで、いくつでも中間地点を指定できます。次の例は、半分の地点で透明度を下げて点滅させる blink です。

style.css
@keyframes blink {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0.2; /* 中間地点でいったん薄くする */
  }
  100% {
    opacity: 1;
  }
}

/* 同じスタイルになる時点はカンマでまとめられる */
@keyframes pulse {
  0%, 100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.2);
  }
}

同じスタイルを複数の時点で指定したいときは、0%, 100% のようにカンマで区切ってまとめられます。pulse の例では、開始と終了で元の大きさ、中間で 1.2 倍に拡大することで、ふくらんでから戻る動きを表現しています。

animation で要素に適用する

定義した @keyframes は、それだけでは何も起きません。動かしたい要素に animation プロパティを指定して、はじめて再生されます。最小の例として、先ほどの spin を 1 秒・等速・無限回で適用してみます。

style.css
.spinner {
  width: 40px;
  height: 40px;
  border: 5px solid #dbeafe;
  border-top-color: #3b82f6; /* 一辺だけ色を変えると回転が見える */
  border-radius: 50%;
  /* spin を 1 秒かけて・等速で・無限に繰り返す */
  animation: spin 1s linear infinite;
}

これだけで、要素がくるくると回り続けるローディングスピナーになります。animation: spin 1s linear infinite; の部分が一括指定で、それぞれの値が何を意味するのかは次の節で整理します。

animation のサブプロパティ

animation は、いくつかの個別プロパティをまとめた一括指定(ショートハンド)です。それぞれを個別に書くこともできます。まずは各プロパティの役割と代表的な値を整理しておきましょう。

プロパティ役割と主な値
animation-name適用する @keyframes の名前。spin など定義した名前を指定する。
animation-duration1 回の再生にかける時間。1s500ms のように秒・ミリ秒で指定する。
animation-timing-function時間内での速さの変化(イージング)。ease / linear / ease-in-out など。
animation-delay再生が始まるまでの待ち時間。0.3s なら 0.3 秒待ってから動き出す。
animation-iteration-count繰り返す回数。1(初期値)や 3 などの数値、無限に繰り返すなら infinite
animation-direction再生方向。normal(毎回同じ向き)/ reverse(逆再生)/ alternate(往復)/ alternate-reverse
animation-fill-mode再生前後にどの状態を保つか。none(初期値)/ forwards(終了状態を保持)/ backwards / both
animation-play-state再生中か一時停止か。running(初期値)/ paused。ホバーで止めるなどに使う。

とくに繰り返しの動きで重要なのが animation-iteration-countanimation-direction です。infinite を指定すればスピナーのように止まらず動き続け、alternate を指定すると往路(0% → 100%)と復路(100% → 0%)を交互に再生するため、行って戻ってを自然に繰り返せます。また animation-fill-mode: forwards は、繰り返しのないアニメーションで「終わった状態のまま止めたい」ときに使います。これを指定しないと、再生が終わった瞬間に最初の状態へ戻ってしまいます。

一括指定(ショートハンド)の書き方

実際の制作では、animation プロパティ 1 行でまとめて書くのが一般的です。値はスペース区切りで並べ、順番はおおむね自由ですが、時間に関する 2 つの値だけは順番が意味を持ちます。先に書いた時間が duration、後に書いた時間が delay と解釈されるため、遅延を付けたいときは時間を 2 つ並べます。

style.css
/* name duration timing-function delay iteration-count direction の順で書いた例 */
.spinner {
  animation: spin 1s linear 0s infinite normal;
}

/* よく使う最小構成:名前・時間・繰り返し回数だけ */
.dot {
  animation: blink 1.2s infinite;
}

/* 往復させたいときは alternate を加える */
.pulse {
  animation: pulse 2s ease-in-out infinite alternate;
}

/* 1 回だけ再生し、終了状態を保持したいとき */
.fade {
  animation: fade-in 0.6s ease-out forwards;
}

個別プロパティに分けて書くと、対応関係がはっきりします。読みやすさや、特定の値だけを上書きしたいときには個別指定が便利です。

style.css
.spinner {
  animation-name: spin;                 /* どの @keyframes を */
  animation-duration: 1s;               /* 1 回に何秒かけて */
  animation-timing-function: linear;    /* どんな速さの変化で */
  animation-iteration-count: infinite;  /* 何回繰り返すか */
}

動く例で確認する

言葉だけでは伝わりにくいので、実際に動くデモを用意しました。下のプレビューには 3 つの要素があります。左は infinite で回り続けるローディングスピナー、真ん中は alternate でふわっと現れては戻るラベル、右は 0%〜100% の複数キーフレームで点滅する丸です。CSS タブを開くと、それぞれの @keyframesanimation の指定を確認できます。

<div class="demo">
  <div class="spinner"></div>
  <div class="fade">ふわっと表示</div>
  <div class="dot"></div>
</div>
.demo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 40px;
  flex-wrap: wrap;
  padding: 48px;
  font-family: sans-serif;
}

/* 1. ローディングスピナー:infinite で回転を繰り返す */
.spinner {
  width: 48px;
  height: 48px;
  border: 5px solid #dbeafe;
  border-top-color: #3b82f6; /* 一辺だけ色を変えると回転が分かる */
  border-radius: 50%;
  /* spin を 1 秒・等速・無限回 */
  animation: spin 1s linear infinite;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* 2. ふわっと表示:fade-in を alternate で行き来させる */
.fade {
  padding: 12px 20px;
  color: #fff;
  background-color: #10b981;
  border-radius: 8px;
  /* 1.5 秒かけて、戻りながら無限に繰り返す */
  animation: fade-in 1.5s ease-in-out infinite alternate;
}

@keyframes fade-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 3. 点滅:0%〜100% で複数のキーフレームを指定 */
.dot {
  width: 40px;
  height: 40px;
  background-color: #ef4444;
  border-radius: 50%;
  animation: blink 1.2s ease-in-out infinite;
}

@keyframes blink {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0.2; /* 中間で薄くする */
  }
  100% {
    opacity: 1;
  }
}
Preview

動きすぎを避ける配慮(prefers-reduced-motion)

画面の動きが体調に影響する利用者もいるため、OS には「視差効果を減らす」といった設定があります。CSS では prefers-reduced-motion メディアクエリでその設定を検知でき、動きを抑える配慮ができます。次のように書くと、設定が有効な環境ではアニメーションをほぼ無効化できます。

style.css
/* 動きを減らす設定の利用者には、アニメーションをほぼ止める */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

ローディングスピナーのように動きが意味を持つものは完全には止めにくいですが、装飾目的の派手な動きは控えめにする、といった判断ができると親切です。すべてに対応する必要はありませんが、頭の片隅に置いておくとよいでしょう。

アニメーションが動かない・1 回で止まるときの原因

@keyframes を書いたのに動かない」「一瞬動いてすぐ止まる」という相談はよくあります。原因はだいたい決まっているので、代表的なものを順に確認していきましょう。

@keyframes の名前と animation-name が一致していない

もっとも多いのが名前の打ち間違いです。@keyframes spin と定義したのに、要素側で animation: spinner 1s ... のように違う名前を書いていると、当てはまる定義が見つからず何も起きません。大文字・小文字も区別されるため、fadeInfadein は別物として扱われます。動かないときは、まず両者の名前が完全に一致しているかを見直してください。

iteration-count を指定せず 1 回で終わっている

「一瞬だけ動いて止まってしまう」場合、animation-iteration-count の指定漏れが疑われます。このプロパティの初期値は 1 なので、何も指定しないとアニメーションは 1 回だけ再生されて終わります。スピナーのように繰り返したいなら infinite、決まった回数なら 3 のように数値を指定する必要があります。一括指定なら animation: spin 1s linear infinite; のように infinite を忘れず加えましょう。

終了後に最初の状態へ戻ってしまう

1 回だけ再生するアニメーションで、「動き終わった瞬間にパッと元の見た目に戻る」ことがあります。これは animation-fill-mode の初期値が none で、再生前後は @keyframes の指定が無視されるためです。終了状態をそのまま保ちたいときは animation-fill-mode: forwards を指定します。たとえばフェードインで最後に opacity: 1 のまま表示を保ちたいなら、forwards が必要です。

duration を指定していない

animation-duration の初期値は 0s です。時間を指定し忘れると再生時間がゼロになり、アニメーションが目に見えないまま一瞬で終わってしまいます。一括指定で書くときは、最低でも animation-nameanimation-duration の 2 つは必ず指定するようにしてください。

まとめ

@keyframes でアニメーションの動き(途中経過)を定義し、animation でそれを要素に適用するのが基本の流れです。@keyframesfrom / to または 0%〜100% でキーフレームを書き、animationanimation: spin 1s linear infinite; のように名前・時間・繰り返しなどを 1 行でまとめて指定します。繰り返したいなら infinite、往復させたいなら alternate、終了状態を保ちたいなら forwards が鍵になります。transition がきっかけを必要とするのに対し、animation は自律的に・繰り返し動くのが大きな違いです。動かないときは名前の一致と iteration-countduration の指定を見直し、利用者への配慮として prefers-reduced-motion も頭に入れておくと、より安心して使えます。

参考ページ