Rive(旧Flare)でパスモーフィング

# はじめに
Flutterで利用可能なアニメーションを作成するWebツール、[Rive](https://rive.app/)を使ったアニメーション作成例の記事です。この間まで、Flareという名前だったこのツールですが、先日のFlutter Interact '19でRiveに変わりました。

今回はRiveを使って、SVGのPathを変化させるアニメーション(パスモーフィング)を作り、Flutterに組む方法を紹介します。

# 今回つくったもの
![path_morph_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/8b496c4c-f2b8-13d0-819a-329e1db56ab6.gif)
多角形の頂点数を増やしてアニメーションさせるものです。
4→5→６→7→8と増やしていき、8角形になったら最初の4角形に戻ります。

# Riveでの操作
ここからRiveでの操作の説明になります。

## はじめの状態の図形を作る
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/e2634dea-1da8-ea54-2351-09a5037bc53f.png" width="70%">
+マークから、作成したい図形を作ります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/9dfceb1b-f857-de6d-9842-5c9fb1a6cea9.png" width="70%">
はじめの図形の4角形をPolygonで作りました。

## pathに変更する
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/81a9b2cf-8341-490a-b948-d539f572705b.png" width="70%">
そのままではpathとして扱えないので、`CONVERT TO PATH`でpathに変換します。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/92dd992e-e930-90ec-d5e3-2a738e04a4c8.png" width="70%">
`EDIT VERTICES`でpath頂点の編集ができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/59303b1a-6703-3883-cb9c-218dd0da7a93.png" width="50%">
4角形をpath化した状態だと上記のようにpathの頂点は4つできます。

pathを変形させるアニメーションではpathの頂点の変化を記述していくため、アニメーション中の頂点数が同じである必要があります。
(参考)https://www.webcreatorbox.com/tech/svg-morphing

今回は4角形→8角形に変化させていくため、頂点数をはじめの図形から8つにしておきます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/32d8fdd2-7c38-0f3a-32c4-8a012bcc3212.png" width="50%">
上部の`Pen`を選択することで、頂点を増やすことができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/e8189aa4-2a74-ccb5-0263-3ee5175f9026.png" width="50%">
頂点を8つにしました。


## アニメーションを作る

#### アニメーションの追加/名前設定
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/539d6eb6-58d3-eba8-4c18-02f8e57f9b78.png" width="50%">

タブを`ANIMATE`に移し、アニメーションを作成していきます。
`+`ボタンでアニメーションの追加ができます。
Untitledという名前で作られますが、ここで指定した名前をFlutterのコード内で指定するので、適切な名前を付けておきます。


#### ◆マークをチェックする
![スクリーンショット 2019-12-23 13.28.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/6fef3d52-165a-df04-f695-8b09328abcde.png)
変形させたい図形を選択し、右側ペインの`EDIT VERTICES`横にある、◆マークをチェックします。
◆マークをチェックすることで、ある時間での要素の状態を決めることができます。
ここでは時間00:00:00時点でのpathの形を固定しました。

#### 時間経過後の変形
1秒後に5角形になるようにします。

![スクリーンショット 2019-12-23 13.37.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/9268093c-4e20-b9e8-d7dd-1e80f5a60f5a.png)
下の時間スライダーを00:01:00に動かし、右側ペインの`EDIT VERTICES`横にある、◆マークをチェックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/db3c7976-7f33-1e16-3e40-3a46d67db47a.png)
`EDIT VERTICES`で編集し、完了したら`DONE EDITING`で保存します。

![morph_mac.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/3dff94b6-26fd-5b73-b140-e7a0bd7eb109.gif)
4角形から5角形の変形ができました。

#### 繰り返し
あとは同じ要領で変形するポイントを追加していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/7e091b1e-f673-9243-11f7-10bc8f3f3e0a.png)


# Flutterに組み込む
### Export
Flutterに組み込める形にExportします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/4173f3a1-9d93-2526-c657-9ac90a92f8a0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/45b6deb4-bef6-f5a2-b6db-2a9d8f00511e.png)
右上のボタンよりExportします。FormatはBinaryにします。
.flr形式のファイルがダウンロードされます。

### Flutterプロジェクトに配置
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/ae8556dc-f787-d02a-99e2-4114bf56ccc7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/656a2787-e365-7d29-4567-8c93a213fceb.png)
適当なディレクトリに.flrファイルを配置し、FlareActorで読み込めば、アニメーションが行われます。
FlareActorはこちら (https://pub.dev/packages/rive) をdependenciesに追加することで使えます。中ではflare_flutterに依存していますが、名称変更に伴いラップしたものが用意されたようです。

![path_morph_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/8b496c4c-f2b8-13d0-819a-329e1db56ab6.gif)

# UIと協調させる
上の例では多角形の変化をひとつなぎのアニメーションにしました。
次の例では、ボタン押下時に次の形に変形するようにしてみます。

この場合、アニメーションが1つだと難しかったので、複数のアニメーションに分割することで対応できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/4d5b5934-3ca9-78e4-2b4f-b07668dd7699.png)
このように、多角形1つの変化のみのアニメーションを複数作ります。(画像は5角形→6角形のアニメーション)


コードでは、アニメーションの名前を保持しておき、ボタンが押される度に、setStateで使われるanimationを変更するように実装します。

```dart:
class _AnimationSample02PageState extends State<AnimationSample02Page> {
  final List<String> _animations = [
    '4_to_5',
    '5_to_6',
    '6_to_7',
    '7_to_8',
    '8_to_4',
  ];

  int _animationIndex;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Animation Sample 02'),
      ),
      body: Column(
        children: <Widget>[
          (略)
                child: FlareActor(
                  'images/path_morph.flr',
                  fit: BoxFit.contain,
                  animation: _animationIndex != null
                      ? _animations[_animationIndex]
                      : null,
                ),
          (/略)
          ,
          FloatingActionButton(
            child: Icon(Icons.autorenew),
            onPressed: () {
              setState(() {
                if (_animationIndex == null ||
                    _animationIndex == _animations.length - 1) {
                  _animationIndex = 0;
                } else {
                  _animationIndex++;
                }
              });
            },
          )
        ],
      ),
    );
  }
}
```

# 完成
![path_morph_tap.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20276/56bba9a4-1215-59ea-3b55-d51df90bff5d.gif)

# さいごに
Riveを使って作ったpathを変化させるアニメーションをFlutterに組み込むことができました。
簡単なアニメーションだけでなく、実践的なアニメーションも作れるよう頑張ります。

今回作成したアニメーション、コードは以下にあります。
* Riveのファイル : https://rive.app/a/iiinaiii/files/flare/path-morph-sample-02/preview
* Flutterコード : https://github.com/iiinaiii/Flutter-UI-Samples/blob/master/lib/ui/animation_sample_02.dart

# 参考
https://www.webcreatorbox.com/tech/svg-morphing
https://rive.app/a/nimbbble/files/flare/morphing/preview