Anime.jsをプロダクトで使用する4つの理由

この記事では「Anime.jsをプロダクトで使用する4つの理由」と題して、[Anime.js](https://github.com/juliangarnier/anime/)を実際にプロダクトで使用した中で見えてきたポイントについて、以下の順番でまとめたいきたいと思います。

1. **軽量で扱いやすい**（実装したいアニメーションがシンプルなものだったため）
2. **公式ドキュメントが読みやすい**
3. **チームでの開発/運用がしやすい**
4. **変更が容易**（突発的な仕様変更にも耐えうる）

一つずつ、詳細に見ていきます。

### 理由１: 軽量で扱いやすい

Anime.jsは**シンプルながらもパワフルな、JavaScriptアニメーションライブラリ**です。

GitHubスター数は**39,900**（2021年7月現在）と人気で、また、その機能の豊富さ・手軽かさから、[**はてなブログでも一時期話題となりました**](https://b.hatena.ne.jp/entry/anime-js.com/)。

*はてブスター数は996（2021年7月現在）*

![hatena_star_anime_js.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/857708/f0702f6b-a464-7a2a-dc19-7fa9ed48a6ef.png)

そしてminifyがかかったビルドファイルが[**総容量は17.3kb**](https://github.com/juliangarnier/anime/blob/master/lib/anime.min.js)と、**非常に軽量**です。

またその軽量さとは裏腹に、CSSプロパティ、SVG、DOM操作といった多彩なアニメーションが可能です。

> Anime.js (/ˈæn.ə.meɪ/) is a lightweight JavaScript animation library with a simple, yet powerful API.
It works with CSS properties, SVG, DOM attributes and JavaScript Objects.

### 理由２： 公式ドキュメントが読みやすい

個人的な推しポイントとしては、[**ドキュメントの読みやすさ**](https://animejs.com/documentation/)です。
ライブラリの**使用方法が直感的にわかる、非常に読みやすいドキュメント**となっています。

*↓公式Documentの一部を画面キャプチャしたもの。画面右側にはコードの記述例が書かれており、その動きを画面左側で実際に確認することができる（マウスオーバーすることでアニメーションが動作する）。*
![anime_cap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/857708/f168bc7c-d829-332a-363f-a504c3c008bc.png)

**軽量であるだけでなく、ドキュメントも読みやすく、非常に使いやすそうな点が導入の要因となりました。**
（実際にプロジェクトで使用してみて、混乱するようなことはとても少なかったように感じます。）

### 理由３: チームでの開発/運用がしやすい
アニメーションライブラリの選定にあたっては、パフォーマンスを重要視した上で、それと同じくらいに、**今後もチームで継続的に使用していけそうか**という観点を意識していました。

```bash
# 意識した優先順位
パフォーマンス（速さ、軽さ）  ==  コードの可読性、メンテナンス性(チーム開発に適しているか)  <<<  機能の豊富さ
```

この点に関して、Anime.jsは比較的コードの可読性・メンテナンス性が高いと言えます。
それは**一つ一つのアニメーションをJavaScriptのオブジェクトとして管理**することができるためです。

#### アニメーションをオブジェクトとして管理する

ここでは例として、Anime.jsを使用した簡単なアニメーションの実装を見てみましょう。

```javascript
// 下記のようにアニメーションを定義します。
const nankaUgoki = {
  targets: "#nanka", // DOMを指定. 配列で複数指定することも可能.
  easing: "linear", // easingの設定値は他にも多数あり
  duration: 1000, // 動作にかかる時間を設定
  translateX: 250, // 加えたいアニメーションを記述. 基本はCSSプロパティを設定する.
  rotate: 2, // アニメーションは複数指定可能
  begin: function () {
    // アニメーション開始時に実行される処理を記述する
  },
  complete: function () {
    アニメーション完了時に実行される処理を記述する
  },
  // その他にも多数のプロパティを設定できます。
}

// 実行には anime()メソッドを使用します。
anime(nankaUgoki) // 指定したDOMが動く
```
上記のように、個々の動作をオブジェクト単位で管理することで、**コードのメンテナンスが快適**になりました。

このような**わかりやすいインターフェース**は、**初めてコードを見た人にとっても、理解し易い**ものかと思われます。
また、**動作とターゲットを一つのオブジェクトでまとめて管理できる**のも面白いポイントであり、コードを通して**実際の動作をイメージすることが容易**なのも嬉しい点です。

これらの点が**チームでの開発・メンテナンスを容易にしてくれる**と感じました。

### 理由４: 変更が容易

Anime.jsには、**複数のアニメーションを同期的に連結**する `Timelines` という機能があります。これを使用することで、**個々のアニメーションの実行順序を制御**することができます。


> Timelines let you synchronise multiple animations together.

実行の順序を制御するだけの機能ではありますが、これが可能であることにより、**複雑なアニメーションを、小さなアニメーションの集合体として分割する**ことができます。

#### アニメーションを連結する

以下の例では、「小さなアニメーション」を連結して、「連続した複雑なアニメーション」を実装しています。

```javascript
// timelineを初期化する
const renzokuShitaUgoki = Anime.timeline({
  easing: "linear", // 初期値を設定可能
  duration: "1000", // addした個々の動きは初期値を継承する
});

// 小さな動きを作っておく
const tiisanaUgoki01 = {
  targets: '#nanka', 
  duration: 2000, // 初期値が上書かれる
  translateX: 250,
}
const tiisanaUgoki02 = {
  targets: '#nanka', 
  duration: 500,
  translateY: 100,
}

// timelineに `add` メソッドを使って動きを追加する. 追加した順番にアニメーションが行われる.
renzokuShitaUgoki.add(tiisanaUgoki01).add(tiisanaUgoki02);
// この場合は、2000msかけてX方向に250px移動した後、500msかけてY方向に100px移動する。
```

大きなアニメーションをイチから作り上げるのではなく、**小さなアニメーションを組み合わせて一つの大きなアニメーションを組み上げる**イメージです。

一つ一つの動作がオブジェクト化されているため、**部分的に動作を追加したり、また調整したりといったことが行いやすい**ため、とても開発者フレンドリーな機能だと言えます。

実装途中に仕様変更が発生しても、該当箇所に対応するオブジェクトを調整することで、**柔軟な対応を行うことが可能**です。

## 詰まったところ

開発中に何点か詰まったところがあったため残しておきます。

#### 動きが複雑になってくると実装が難しい
例えば、`translateX: ["0", "100%"]` のように開始位置と終了位置を定義することはできても、`translateX: ["0", "40%", "100%"]` のように中間点を追加することができません。

```javascript
// OK!
anime({
  targets: '#nanka', 
  translateX: ["0", "100%"], // 開始位置と終了位置を設定可能
})

// NG!!
anime({
  targets: '#nanka', 
  translateX: ["0", "40%", "100%"], // しかし中間点を追加することはできない
})
```

このように細かく動きを調整する場合は、工夫が必要となってきます（例えば["0", "40%"]のオブジェクトと["40%", "100%"]のオブジェクトの二つに分割して連続で動作させるなど）。

#### CSSのdisplayプロパティの操作ができない
`anime({ display: 'none' })` という制御が効きません。
そのため、**とあるタイミングでDOMを表示/非表示した...**というときには、`begin` や `complete` といったプロパティを使用したり、あるいは `opacity`を使用するといった代替表現をする必要があります。

```javascript
// NG!
anime({
  targets: '#nanka', 
  duration: 300,
  display: 'none', // 300ms後にふわっと要素を消したい => NG!
})

// OK!!
anime({
  targets: '#nanka', 
  duration: 300,
  opacity: 0, // 300ms後に要素を透明にし、
  complete: function () {
    // 完了後に直接DOMを消す
    document.querySelector('#nanka').style.display = 'none';
  }
})
```
[参考： StackOverFlow](https://stackoverflow.com/questions/55100314/change-css-display-property-using-animejs)


## おわりに

本記事では「Anime.jsをプロジェクトで使用する4つの理由」として、Anime.jsをプロダクトの中で使用した際によかったと感じた点について記しました。

ライブラリを選定する際には、他の[Canvasライブラリ](https://p5js.org/)や[Adobe Animate](https://www.adobe.com/jp/products/animate.html)など多様なものを比較しましたが、今回はAnime.jsがうまくはまりました。

必ずしも万能なライブラリとは言い切れませんが、使い所にマッチすれば、非常に強い力を発揮するでしょう。