【CSS animation】CSSだけで作るループアニメーション

## これは何

CSSだけで、ループアニメーションを作る方法と仕組みについてまとめました。

CSSでループアニメーションをする方法を解説する記事はいろいろありますが、それぞれ微妙に内容が違ったり使いたい条件によってカスタムが必要だったので、ちゃんと仕組みを理解をしようと思い整理しました。

:::note warn
動き続ける要素があると、本来集中したいコンテンツに集中できないなどの弊害もあるので、使い所は要検討しましょう。
また、ストップボタンなどを用意してアニメーションを停止できるようにするのがおすすめです。
:::

詳しくは[WCAG2.1の達成基準 2.2.2 一時停止、停止、非表示](https://waic.jp/translations/WCAG21/#pause-stop-hide)やUnderstanding WCAG 2.1の[達成基準 2.2.2: 一時停止、停止、非表示を理解する](https://waic.jp/translations/WCAG21/Understanding/pause-stop-hide.html)などをご覧ください。

## （基礎知識） CSS animation とは

CSSだけで簡単にアニメーションが作れるプロパティです。

`transiton`との大きな違いは以下の3点です

1. アニメーションが発火するタイミング
1. アニメーションを指定できるプロパティ
1. `@keyframes`が使える

`transiton`は、要素に何らかの変化があったときにその変化の間を埋めるアニメーションを作ります。
例えば、ホバーして色が黒から緑に変わる時に、ゆっくりと色を変化させるなどです。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577501/7dbbdbe4-e703-9ec5-9c2f-751004f8458a.png)

`animation`では要素が読み込まれた時にアニメーションが始まり、キーフレームごとのスタイルを指定してアニメーションさせることができます。
例えば、常に回転していて一定間隔で色が変わるオブジェクトなどの装飾的なアニメーションが作れます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577501/0aaf7e97-76eb-8bed-f808-9edba850ee4b.png)

アニメーションを実行するか停止するかも指定できるので、「hoverをしている時だけアニメーションさせる」といったことも可能です。

ざっくりと`transition`と`animation`のできることの違いを以下の表にまとめてみました。

種類 | `transition` | `animation`
-|-|-
発火タイミング | 指定したプロパティ`property` が変化する時 | 要素が読み込まれた時
待ち時間 `delay`| ○ | ○
実行時間 `duration`| ○ | ○
イージング `timing-function`| ○ | ○
アニメーションの実行/停止 `play-state` | × | ○
再生の向き `direction` | × | ○
アニメーションの再生回数 `iteration-count` | × | ○
アニメーション実行前後のスタイル適用 `fill-mode` | × | ○
アニメーションをキーフレームごとに設定 `@keyframes` | × | ○ 

### CSSだけでアニメーションがループする仕組み

基本的には、画像を「端から端に動かす」アニメーションを「繰り返す」ことでループしているように見せます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577501/d4173d8c-1ffc-01cb-4162-495fe6dd4756.png)

途切れずにアニメーションしているように見せたい時は、ループさせるエリアより大きいオブジェクトを2つ用意します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577501/4f7fcba3-0be7-8b3c-1828-9d2107cac8d0.png)

実際に動かしてみたものは以下です。（目がチカチカするので、折りたたんでます）

<details><summary>ループアニメーションのデモ</summary>

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="QWYOQLE" data-user="kabechiyo" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kabechiyo/pen/QWYOQLE">
  Untitled</a> by kabechiyo_shunkaaizawa (<a href="https://codepen.io/kabechiyo">@kabechiyo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

</details>

応用すると、こんな感じで回転するループの動きも作れます。

<details><summary>回転しながらループするアニメーションのデモ</summary>

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="vYbpmBQ" data-user="kabechiyo" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kabechiyo/pen/vYbpmBQ">
  Untitled</a> by kabechiyo_shunkaaizawa (<a href="https://codepen.io/kabechiyo">@kabechiyo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

</details>

## ループアニメーションの作り方

ようやくアニメーションの作り方です。

### 一個だけの要素を動かすとき

まずはループさせる要素と、ループさせるエリアを用意します。

```html
<section class="loop_area">
  <div class="loop_right">
    ループする要素
  </div>
</section>
```

次に、ループさせるエリアに適当なwidthを指定します。（100%でもOK）
はみ出す部分は表示されないようにしたいので、`overflow: hidden`も指定します。

```css:ループさせるエリア
.loop_area {
  width: 400px;
  overflow: hidden;
}
```

次にループアニメーションを作っていきます。
`@keyframes`で0%の時は`translateX(0)`、100%の時に`translateX(100%)`になるようにします。

`animation`でループアニメーションの秒数を指定し、`infinite`でアニメーションを繰り返すようにします。
`width: 100%`を当てて、`translateX(100%)`の時にちょうどエリア外に到達するようにします。

```css:ループアニメーション
@keyframes loop_animation_right {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(100%);
  }
}

.loop_right {
  animation: loop_animation_right 3s linear infinite;
  width: 100%;
}
```

これでループアニメーションの完成です。
「Result」でアニメーションの様子を見れます。

ループする向きを逆方向にしたいときは、`animation-direction: reverse;`を指定するか、`@keyframes` の値を`0%`の時に`transform: translateX(100%);`・`100%`の時に`transform: translateX(0);`に変更します。

<p class="codepen" data-height="300" data-default-tab="html" data-slug-hash="poGVVoa" data-user="kabechiyo" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kabechiyo/pen/poGVVoa">
  loop_animation_right</a> by kabechiyo_shunkaaizawa (<a href="https://codepen.io/kabechiyo">@kabechiyo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### ずっとループしているように見せるとき

先ほどと同じようにループするエリアを作り、中にループさせたい要素を「ループエリアよりも大きいサイズ」で「2つ」作ります。

```html
<section class="wrapper">
  <div class="loop_right_double">
    ループ ループ ループ ループ ループ ループ ループ ループ ループ
  </div>
  <div class="loop_right_double">
    ループ ループ ループ ループ ループ ループ ループ ループ ループ
  </div>
</section>
```

先ほどと同じくループさせるエリアに`width`と`overflow: hidden;`を指定します。
また、ループさせる要素が横並びになるように`display: flex;`を指定します。

```css:ループさせるエリア
.wrapper {
  display: flex;
  width: 400px;
  overflow: hidden;
}
```

そして、アニメーションを作っていきます。
今回ははじまる位置を`translateX(-100%)`にし、最初は「2個目」の要素が見えているようにし、だんだん「1個目」の要素が見えてくるようにします。

また、今回はテキストをループさせているので、テキストが折り返さないように`white-space: nowrap;`を指定しておきます。

```css:ループアニメーション
@keyframes  loop_animation_right_double {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(0);
  }
}

.loop_right_double {
  animation: loop_animation_right_double 3s linear infinite;
  white-space: nowrap;
}
```

これでループアニメーションの完成です。
「Result」でアニメーションの様子を見れます。（分かりやすいように2個目の要素の色を変えてます。）

<p class="codepen" data-height="300" data-default-tab="html" data-slug-hash="vYbjjGN" data-user="kabechiyo" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kabechiyo/pen/vYbjjGN">
  Untitled</a> by kabechiyo_shunkaaizawa (<a href="https://codepen.io/kabechiyo">@kabechiyo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### 【番外編】回転しながらループするアニメーション

練習がてら遊びに作ってみました。
今までのループは`transform`で作っていましたが、こちらは`top, left`で位置を指定しています。

これは、端まで行った時にエリア外にはみ出すのではなくエリア内で止めるためです。

例えば右端まで行った時の位置は、`left: 100%`でエリア外にはみ出した位置に持っていき、`transform: translateX(-100%)`を重ねて自分のサイズ分左にずらすことで、エリア外にはみ出さない位置を指定しています。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577501/cb79201b-c647-eefb-5379-070bd58f38a3.png)

動かしてみたものはこちらです。「Result」でアニメーションが確認できます。

<p class="codepen" data-height="300" data-default-tab="html" data-slug-hash="vYbpmBQ" data-user="kabechiyo" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kabechiyo/pen/vYbpmBQ">
  Loop_circle</a> by kabechiyo_shunkaaizawa (<a href="https://codepen.io/kabechiyo">@kabechiyo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>