Web Animations API でアニメーションしてみる

## はじめに
皆さん、Web Animations API というものはご存知でしょうか？
JavaScript でも CSS の keyframe でアニメーションをするのと同じような書き味で、アニメーションを実装することができるAPIです。

この記事では、Web Animations API の`composite`という機能が Safari の最新機能を試せる Technology Preview 142 でサポートされるとのことで、その使い方についてまとめてみました。

https://developer.apple.com/safari/technology-preview/release-notes/

## 目次
- 概要
- 使い方
  - animation定義 
  - コントロールメソッド
  - デモ①（基本定義・コントロールメソッド）
  - アニメーションの重ねがけ
  - デモ②（重ねがけ）
- まとめ

## Web Animations API
### 概要
Web Animations API（以下、WAA）は、JavaScript を使ってアニメーションを構築したりプレイバックを制御することが出来ます。

ブラウザ対応としては、Firefox 48 以降および Chrome 36 以降で標準機能として利用可能です。
それ以外のものについては必要に応じて polyfill を追加します。

https://developer.mozilla.org/ja/docs/Web/API/Web_Animations_API/Using_the_Web_Animations_API#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88

### 使い方
#### animation定義
```
element.animate(keyframes, options)

- keyframes: アニメーションの開始や終了の状態
- options: 実行回数や時間の設定
```
WAAでは、取得したDOMに対して`animate()`を使用してkeyframesとoptionsを指定していくのですが、CSSのkeyframeと同じような感じで、より細かい調整を書くことができます。

keyframesはオブジェクトの配列で指定し、`offset`では0〜1の間でキーフレーム（どのタイミングでその状態になるか）の設定します。
例えば以下のようなコードであれば、対象のDOMが 「1000ms(1秒)の間で少しずつ出現し、さらに次の1000msで透過していく」 ようなアニメーションとなります。
```js
Element.animate(
  [
    {
      opacity: '0', // 透過０
      offset: 0, // キーフレームの設定
      easing: 'ease-in-out'
    },
    {
      opacity: '1', // 透過1
      offset: 0.5,
      easing: 'ease-in-out'
    },
    {
      opacity: '0', // 透過０
      offset: 1,
      easing: 'ease-in-out'
    }
  ],
  {
    duration: 2000, // アニメーションにかける時間をmsで指定
    fill: 'forwards',
  }
);
```

#### コントロールメソッド
また、WAAではアニメーションのプレイバックを制御することが可能です。
アニメーションを定義したDOMに対し以下のメソッドを実行することで、アニメーションを止めたり倍速にしたりできます。
```
element.play() : 再生する
element.pause() : 一時停止する
element.reverse() : 逆再生する
element.cancel() : 再生終了
element.finish() : 終了時点まで進める
element.playbackRate : 再生速度のプロパティ（値を 2 にすれば2倍速になる）
```

#### デモ①（基本定義・コントロールメソッド）
上記の animation定義 と コントロールメソッド のデモがこちらです。
CSSでアニメーションした場合とコードの比較ができます。
※ startボタン→アニメーション開始/再開、pauseボタン→一時停止、reverseボタン→逆再生、morefastボタン→1.5倍速
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="RwPGXvE" data-user="artm512" 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/artm512/pen/RwPGXvE">
  balloon</a> by artm512 (<a href="https://codepen.io/artm512">@artm512</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

#### アニメーションの重ねがけ
WAAでは、要素に対して複数のアニメーションをかけることが可能です。
例えば、X方向に600px移動しながらY方向に上下運動し、さらに拡大縮小を繰り返す、、、みたいな複数のアニメーションを対象のDOMに指定できます。

CSS の animationを使用してそれを実装したい場合、それぞれのアニメーションに対してDOMを入れ子にする必要があり、

```html
<div class="anim_x">
  <div class="anim_y">
    <div class="target anim_scale"></div>
  </div>
</div>
```

上記に対してそれぞれアニメーションを指定するイメージになるかと思います。

しかし、WAAを使用すればそれぞれのアニメーションを
```html
 <div class="target"></div>
```
に対してまとめてかけられるので、余計なDOMを増やさずに済みます。

そしてそれを実現するために登場するのが、「はじめに」で出てきた `composite`という機能です。

keyframeにそれぞれ指定すると、指定したアニメーションを重ね合わせることができます。
```js
// X方向への移動
Element.animate([
    { transform: "translateX(0px)"},
    { transform: "translateX(600px)" }
  ], {
    duration: 4000,
    composite: "add", // keyframeを重ねる
    fill: "forwards",
    easing: "linear"
})
// Y方向の上下運動
Element.animate([
    { transform: "translateY(0px)" },
    { transform: "translateY(400px)" }     
  ], {
    duration: 800,
    composite: "add", // keyframeを重ねる
    fill: "forwards",
    direction: "alternate",
    iterations: Infinity,
    easing: "cubic-bezier(.47,0,.99,.54)"
})
// 拡大縮小
Element.animate([
    { transform: "scale(2)"},
    { transform: "scale(1)" }
  ], {
    duration: 800,
    composite: "add", // keyframeを重ねる
    direction: "alternate",
    easing: "ease-in-out",
    iterations: Infinity
})
```
`composite` で add を指定しない場合、一番最後に定義したkeyframeのみが適用されます。

#### デモ②（重ねがけ）
アニメーション重ねがけのデモです。
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="KKZZWZW" data-user="artm512" 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/artm512/pen/KKZZWZW">
  web animation api - demo</a> by artm512 (<a href="https://codepen.io/artm512">@artm512</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


## まとめ
WAAを使うメリットをまとめると以下になります。
- 細かいタイムライン制御が可能
- アニメーション表現と実行をJSのみでできるので、場合によってはコード管理が楽にすむ
- 複数のアニメーションを重ねられるので、入れ子にするDOMを増やさなくて済む


基本的には、単純なアニメーション（直線移動のものとか）は普通にCSSで書いた方が良いかと思いますが、複雑なアニメーションが入る場合は非常に有用かと思います。


## 最後に
現状Polyfillを入れればどのブラウザでも動きますが、将来的にsafariに標準サポートされればより使える機会が増えてくるかもしれません。

CSSだけで複雑なアニメーションを実装するとコードが冗長になったり、場合によっては負荷が高くなってカクついたりもします。
JavaScriptでアニメーションをする引き出しを増やしておき、よりパフォーマンスの良い実装を選択できると良いですね。

## 参考
- [JavaScript でアニメーション Web Animation API](https://www.webdesignleaves.com/pr/jquery/web-animation-api-basic.html)
- [Web Animations API を利用する](https://developer.mozilla.org/ja/docs/Web/API/Web_Animations_API/Using_the_Web_Animations_API)