Flutter Web VS JavaScript (アニメーション対決)


先日、Flutter Webを利用して動きがあるウィジェットを作りました。

1年分のデータを1日ずつ四角形の色で表示するのですが、ただ表示しても退屈なのでちょっとした動きがあるといいかもと思ってアニメーションを追加してみました。

こういう感じの、四角形をいっぱいならべて、じょじょに表示されるというものです。
![イメージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/d8423eb1-3adb-998d-7c5e-d5e99beeb63e.png)

やってはみたものの、 `おもったよりモッサリ` してしまったので、最終的にはアニメーションを消して利用しました。

Flutterが想定しているアニメーションの使い方ではなかったり、実装方法がまずいのかもしれないですが、 `おもったよりモッサリ` がどんなものだったかをアウトプットしておこうと思います。

## 実施内容

私の頭の中にはJavaScriptによる実装が前提にあって、それと比較して `おもったよりモッサリ` と感じているので、JavaScriptで実装した場合とFlutter Webで実装した場合をデモで比較します。

両者異なる技術なので全く同じ実装は難しいのですが、以下の点を揃えています。

- アニメーションする四角形は同じサイズの正方形とする `20px × 20px`
- 1行あたり同じ数の正方形を並べる `50個`
- 50個ずつ順番にdelayを設定する `n番目のdelay = (1.0 - 行番号)(n/50*50)^2 + 行番号 * 0.1;`
- 同じeasing関数を設定する `cubic-bezier(0.4, 0.0, 0.2, 1.0)`
- 同じdurationを設定する `1200ms`
- ボタンをクリックしたら動的に正方形を生成して画面に配置する

アニメーションの実現方法は両者で異なります。

- JavaScript: 生成したspanにあらかじめ定義したkeyframeアニメーションを設定する
- Flutter Web: 生成したContainerをFadeTransitionとScaleTransitionのchildに設定する

## 結果

### 1回戦：50個 × 1行

私のMacBock AirとChromeでは遜色ないように見えました。

- [JavaScript Demo](https://k38.github.io/vanillaJs/animation_fadein/1.html)
- [Flutter Web Demo](https://k38.github.io/flutter/animation_fadein/build/#/1)

### 2回戦：50個 × 2行

まだ大差ないように見えます。

- [JavaScript Demo](https://k38.github.io/vanillaJs/animation_fadein/2.html)
- [Flutter Web Demo](https://k38.github.io/flutter/animation_fadein/build/#/2)

### 3回戦：50個 × 5行

Flutter Webがかなり辛そうです。

- [JavaScript Demo](https://k38.github.io/vanillaJs/animation_fadein/5.html)
- [Flutter Web Demo](https://k38.github.io/flutter/animation_fadein/build/#/5)

### 4回戦：50個 × 10行

JavaScriptはまだいけます。
Flutter Webは試合を放棄したように見えます。

- [JavaScript Demo](https://k38.github.io/vanillaJs/animation_fadein/10.html)
- [Flutter Web Demo](https://k38.github.io/flutter/animation_fadein/build/#/10)

## 感想

### パフォーマンスについて

JavaScript側のアニメーションの演算は、CSSを介してブラウザのバイナリが効率良くやってるのだと思うのですが、Flutter Webはdartから生成されたJavaScriptがアニメーションの変化量の計算や、自前のレンダリング処理をしたりで、ブラウザに対する負荷が大きくなるのだろうなと思います。
処理状況にどのような違いがあったのか、4回戦の状況をchromeの開発者ツールでパフォーマンス計測してみました。

#### JavaScript

JavaScriptはメインスレッドに隙間があり、アニメーションの振る舞い同様に余裕を感じます。
高負荷なアニメーション処理はAnimationスレッドに振り分け、ラスタライズ処理は最後のみ発生しています。
![javascript.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/f4cec665-bbe0-73e6-8397-2e026c9282b1.png)

#### Flutter Web

Flutter Webはメインスレッドが常に何かの処理で詰まってしんどそうです。
一部Animationスレッドが使われていますが、タイミングからボタンをクリックした際のリップルエフェクトのみに使われた様子です。
ラスタライズ処理で常に2スレッドが実行され続けているように見えます、何をやってるのでしょうか。
![flutter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/664cfd27-7e11-36b9-675b-721232c27410.png)

### コードの記述量について

対決に使った4画面とメニューを表現する為のコード量を数えてみました。
合計の行数でみると40行程度の差ですが、HTMLはほとんどコピペなので、JavaScript側は50行程度しか書いた実感が無いです(ちょっとした処理をちょっとした労力で実現できた)。
Flutter Web側はちょっとした処理を書くのに180行近くも書いたという事で、精神的な負担がありました。

#### JavaScript側

HTML: 86行
CSS: 9行
JavaScript: 43行
(合計: 138行)

#### Flutter Web側

Dart: 179行

## 検証環境

```
Google Chrome は最新版です
バージョン: 76.0.3809.132（Official Build） （64 ビット）
```

```
$ flutter --version
Flutter 1.7.8+hotfix.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 20e59316b8 (8 weeks ago) • 2019-07-18 20:04:33 -0700
Engine • revision fee001c93f
Tools • Dart 2.4.0
```

```
$ webdev --version
2.5.0
```

## まとめ

Flutter Webは現状、ドロワーの開閉やHeroの振る舞いなど、標準ウィジェットでもアニメーションはきついです。
アニメーションを使う部位はローディングアイコンだけにする等、画面内のごく一部にとどめる必要があるかもしれません。
その辺の見極めもつけられるように、Flutter Webを使ってみます。