自分用メモ：CSS・jQuery・WebGLアニメーション比較

#目的
『ノベルゲーム制作』に利用することを目的として、アニメーションライブラリで何を導入すべきかを検討する。
よって、実験のアニメーションは実際に使いそうな「キャラクターの登場【フェード＆ムーブイン】」とする。

重要なのは、描画の滑らかさとコードの扱いやすさ。
具体的な比較としては、**描画の滑らかさ**に観点をおきます。

##環境
使用ツール
・**Electron**（デスクトップ上で実行するゲームを想定）
動作環境
・OS：Windows7 64bit
・CPU：Intel(R) Core(TM)i7-4790 @ 3.60GHz
・RAM：16.0GB


#比較概要

##実験内容
以下のアニメーションを5回行って、その様子を
・視覚的に確認
・デベロッパーツールを用いて調査

![1.PNG](https://qiita-image-store.s3.amazonaws.com/0/166456/041254dc-e4ef-868f-788d-4dae7f482f8a.png)


##注目点
1. 描画の滑らかさ（目で確認）
2. FPSの変化具合
3. CPU使用量の具合
4. メモリ使用量の具合

##比較対象
###[jquery.keyframes.js](http://memo.sdn-project.net/post/138328743539/jquerykeyframes)
>CSSアニメーション処理の animationプロパティ および @keyframes によるキーフレーム設定を、jQueryで動的に処理するプラグイン。CSSに事前に記述する必要がなくjQueryで全て完結させるため、簡単に要素にアニメーションを付与することが可能。


###[anime.js](http://animejs.com/)
>柔軟で軽いJavaScriptのアニメーションライブラリ。CSS、個々のTransform、SVG、DOMの属性、JavaScriptのオブジェクトに対応している。

###[TweenMax.js](https://greensock.com/docs/TweenMax)
>単純な動作から回転、変形、バウンドなどといった複雑な動作も細かく設定することができる。そして複雑な処理をこなしながらも安定した動作と処理の速さが両立されている。

###[CSSのtransition](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions)
>CSSのtransitionプロパティを用いたアニメーション。特にライブラリは使用せず、jQueryの『.css』メソッドを利用してアニメーションを実装する。アニメーションは完全にCSS依存であり、CPUとGPUを使い分けてくれるため、パフォーマンスは良さそう。

###[PixiJS(V4)](http://www.pixijs.com/)
>WebGLを使った2D描写ライブラリ。WebGL2D描画ライブラリの中でも高速で有名。このライブラリを利用したアニメーションでは自動的にWebGL（GPU処理）が適応されるため、処理の高速化が望める。WebGL非対応の場合には、HTMLCanvas（CPU処理）に切り替えてくれる。
詳細は「[最速の2D描画JavaScriptライブラリは!?](https://ics.media/entry/201)」をご覧になるのが良いかと思います。

##実装コード

###jquery.keyframes.js
```js:keyframes
// $test：キャラクター画像が描画されているdivレイヤ

let flag = 0;
$("body").on("click", () => {
	if(flag == 0){
		$test.keyframes({
			translateX: 300,
			opacity: 1,
		}, {
			duration: 1000,
			easing: "linear",
			// 次の記述をすれば、コールバックのCSS調整は不要
			// fill: "forwards"
		}, function(){
			$(this).css("animation", "").css({
				translateX: 300,
				opacity: 1,
			});
		});
		flag = 1;
	}
	else{
		$test.keyframes({
			translateX: 0,
			opacity: 0
		}, {
			duration: 1000,
			easing: "linear",
			// 次の記述をすれば、コールバックのCSS調整は不要
			// fill: "forwards"
		}, function(){
			$(this).css("animation", "").css({
				translateX: 0,
				opacity: 0
			});
		});
		flag = 0;
	}
});
```

###anime.js
```js:anime.js
// $test：キャラクター画像が描画されているdivレイヤ

let flag = 0;
$("body").on("click", () => {
	if(flag == 0){
		anime({
			targets: "#test",
			duration: 1000,
			easing: "linear",
			translateX: 300,
			opacity: 1
		});
		flag = 1;
	}
	else{
		anime({
			targets: "#test",
			duration: 1000,
			easing: "linear",
			translateX: 0,
			opacity: 0
		});
		flag = 0;
	}
});
```

###TweenMax.js
```js:TweenMax
// $test：キャラクター画像が描画されているdivレイヤ

let flag = 0;
$("body").on("click", () => {
	if(flag == 0){
		TweenMax.to(
			$test, 1.0,
			{
				x: "300",
				opacity: 1,
				ease: Power0.easeNone
			}
		);
		flag = 1;
	}
	else{
		TweenMax.to(
			$test, 1.0,
			{
				x: "0",
				opacity: 0,
				ease: Power0.easeNone
			}
		);
		flag = 0;
	}
});
```

###CSSのtransition
```js:CssTransition
// $test：キャラクター画像が描画されているdivレイヤ

let flag = 0;
$("body").on("click", () => {
	if(flag == 0){
		$test.css({
			transform: "translate3d(300px, 0, 0)",
			opacity: 1,
			transition: "all 1.0s linear"
		});
		flag = 1;
	}
	else{
		$test.css({
			transform: "translate3d(0px, 0px, 0)",
			opacity: 0,
			transition: "all 1.0s linear"
		});
		flag = 0;
	}
});
```

###PixiJS
```js:PixiJS
// $test：キャラクター画像が描画されているdivレイヤ
// stage：PixiJSの描画ステージ
// renderer：PixiJSのレンダラ
// sprite：キャラクター画像

let flag = 0;

// レイヤは可視状態にしておき、キャラ画像自体を完全透過しておく
$test.css("opacity", 1);
sprite.alpha = 0;
// 変更を描画
renderer.render(stage);

// アニメーション進行度（フレーム単位）
let t = 0;
// アニメーション終了フレーム値（1000msを60fps基準でフレームに変換）
let d = 1000 * 60 / 1000;

$("body").on("click", () => {
	if(flag == 0){
		animation_fore();
		flag = 1;
	}
	else{
		animation_back();
		flag = 0;
	}
});

function animation_fore(){
	// 進行度が実行時間に達したら終了
	if(t == d){
		t = 0;
		return;
	}
	t++;

	// ループ
	requestAnimationFrame(animation_fore);

	// 位置と透過度変更
	sprite.position.set(300 / d * t, 0);
	sprite.alpha = 1 / d * t;
	// 変更を描画する
	renderer.render(stage);
}
function animation_back(){
	// 進行度が実行時間に達したら終了
	if(t == d){
		t = 0;
		return;
	}
	t++;

	// ループ
	requestAnimationFrame(animation_back);

	// 位置と透過度変更
	sprite.position.set(300 - (300 / d * t), 0);
	sprite.alpha = 1 - (1 / d * t);
	// 変更を描画する
	renderer.render(stage);
}
```

#実験結果
##jquery.keyframes.js
![2.PNG](https://qiita-image-store.s3.amazonaws.com/0/166456/2512e0dd-9055-a717-c28f-9f66d8c3a123.png)

##anime.js
![3.PNG](https://qiita-image-store.s3.amazonaws.com/0/166456/1ce8a65b-8c61-5121-c39a-6f56630a6989.png)

##TweenMax.js
![4.PNG](https://qiita-image-store.s3.amazonaws.com/0/166456/d7a57338-9c17-7257-9fa6-42e22a2e5ad2.png)

##CSSのtransition
![5.PNG](https://qiita-image-store.s3.amazonaws.com/0/166456/a53348a1-3974-4cf0-7107-6d23dec7b369.png)

##PixiJS
![6.PNG](https://qiita-image-store.s3.amazonaws.com/0/166456/27bd4b20-3de8-c908-d3a7-d7a5625bb500.png)

#まとめ
・○：良好
・△：少々不安定（あまり気にはならない程度）
・×：気になる程度、他と比べて低パフォーマンス

|  | keyframes | anime | TweenMax | transition | PixiJS |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|<font color=red>見た目</font>| <font color=red>○</font>  | <font color=red>△</font>  | <font color=red>△</font>  | <font color=red>○</font>  | <font color=red>○</font>  |
| FPS | ×  | ○ | ○  | × | ○ |
| CPU | ○ | ×  |  × | △ | × |
| メモリ | ○ |  × |  × | ○ | ○ |

※『見た目』はカクつき度合い、つまり「滑らかに見えるか」に着目 
・○：カクつきなし、十分滑らか
・△：細かい振動を時折感じる


#結論
正直なところ、この5種で描画の滑らかさに「大きく差が出る」ということはありませんでした。強いていうならば、『anime.js』『TweenMax.js』のアニメーションだけは、カクつきとは言わないまでも僅かに描画が遅れているような振動感があった……という程度です。

一応この検討を通じて、最も描画が安定していると感じたのは**『jquery.keyframes.js』**です。FPSが上下しているのは気にかかりますが、見た目的にはカクつくことがほとんどありません。また他と比べてCPU使用量が少ない点も良いですね。

##ノベルゲームに実装するなら
ノベルゲームに実装するなら**CSSを用いたアニメーション**が適していると感じました。簡単に実装できる上、描画パフォーマンスが高いからです。

jQueryアニメーションは、そのままだとCSSに比べて描画パフォーマンスが低いですが、ライブラリを利用すればパフォーマンスも操作性も向上します。少し複雑な動作を実現させようという目的がある場合には導入を検討したいですね。

WebGLを用いたアニメーションは、パフォーマンスは高いものの扱い方が難しい……というか自由度が高すぎて逆に扱いにくい印象。パーティクルなどの高度なアニメーションでこそ本領を発揮するという感じでしょうか。