[CreateJS] Tween.pause()とTween.pausedを間違えると詰まる

## 先に結論だけ

`Tween.pause()`関数は再生中のTweenアニメーションを停止する関数ではありません。
再生中のTweenアニメーションを停止したい場合は、以下のように指定しましょう。

⭕️ アニメーションの即時停止

```js
Tween.paused = true
```

## はじめに

[TweenJS](https://www.createjs.com/tweenjs)には`.pause()`関数と`.paused`プロパティというよく似た名前のメンバーがあります。この2つはまったく違う機能です。この点が頭から抜けていると予期せぬ挙動で詰まることがあります。詰まりました。

この記事はこの2つの機能を確認、共有するためのものです。

### 想定する環境

この記事は以下のバージョンのTweenJSを前提として書かれています。バージョンが異なると機能が変化している場合があります。記事を読む前にバージョンの確認をしてください。

```json
    tweenjs : "1.0.2"
```

### 想定する読者

この記事は現在CreateJSやTweenJSを利用しているユーザーを想定しています。インストールガイドなどは含みませんのでご了承ください。

## 問題の機能

この記事で問題とするのは、以下のような記述をした場合の機能の差です。

```js
const tween = createjs.Tween.get(circle)
  .to({ alpha: 0, y: 225 }, 10000)

tween.pause(); // <- ここではアニメーションが止まらない。
tween.paused = true; // <- ここでアニメーションが止まる。
```

ユーザーの操作をトリガーに、アニメーションの中断や再開をすることはよくあります。しかし中断が正常にできないと、以下のような問題が発生します。

-   アニメーションが多重起動する。
-   ターゲットの状態が変更され続ける。その間他の操作を受け付けない。
-   メモリリークする。

とくにloopするアニメーションは、正常に中断できないとバグの温床となります。

## 何が起きているか

それぞれの関数とプロパティを追って、実際の処理を確認します。

### Tween.pause()

まずはTween.pause()関数の公式ドキュメントを読んでみます。
[公式ドキュメント : Tween.pause()](https://createjs.com/docs/tweenjs/classes/Tween.html#method_pause)

> Adds an action to pause the specified tween.

ポーズのためのアクションを追加します、とありますがこの文面では直感的に機能を理解できません。ドキュメントにはソースコードへのリンクがありますので、どのような処理を行なっているのか確認します。

[ソースコード : Tween.pause()](https://createjs.com/docs/tweenjs/files/tweenjs_Tween.js.html#l574)

pause関数は、Tweenクラス内部の\_addAction関数を呼び出しています。変更するパラメーターとして`{paused:true}`というオブジェクトを渡しています。

[ソースコード : Tween.\_addAction()](https://createjs.com/docs/tweenjs/files/tweenjs_Tween.js.html#l809)

\_addAction関数は、TweenActionというインスタンスを生成し、Tweenインスタンスのメンバー変数に保存しています。

[ソースコード : TweenAction](https://createjs.com/docs/tweenjs/files/tweenjs_Tween.js.html#l859)

TweenActionはTween内のプライベートクラスです。アクションが実行される時間`t`と、その時に実行される関数`func`を保存しています。TweenActionは`prev`と`next`変数にTweenActionインスタンスを抱えており、チェーン状に参照が続いています。pause()関数で登録されるTweenActionは、規定の時間`t`になるとpausedパラメーターをtrueに変更します。

#### 再生中のTweenをpause()するとどうなるか

現在再生中のTweenで`pause()`を実行すると、TweenActionチェーンの末尾にpauseアクションが追加されます。すべてのアクションが完了した段階で、pauseアクションが呼び出されます。結果としてアニメーションの再生は停止しません。

#### \_addActionを行う関数

pause()以外にも\_addAction()を行う関数があります。
以下の関数が`Tween._addAction()`を行う関数です。

-   play() : 引数に指定したTweenを再生する。
-   pause() : 引数に指定したTweenを停止する。
-   set() : 引数に指定したプロパティをTweenターゲットに反映する。
-   call() : コールバック関数を呼び出す。

これらの関数で指定したプロパティはその瞬間には反映されず、TweenActionチェーンに登録されます。

play()とpause()関数は、実際には`{paused:true or false}`というプロパティを渡すset()関数のラッパーです。

### Tween.paused = true

Tween.pausedプロパティは[Tweenのコンストラクタ内](https://www.createjs.com/docs/tweenjs/files/tweenjs_AbstractTween.js.html#l291)で\_setPaused, \_getPausedというアクセサメソッドに接続されています。

[\_setPaused関数](https://www.createjs.com/docs/tweenjs/files/tweenjs_AbstractTween.js.html#l238)は、`createjs.Tween._register()`という関数でTweenインスタンスの登録と解除を行ないます。登録解除されたTweenインスタンスは、createjsのTickerで処理されなくなり、アニメーションはその瞬間に停止します。

## なぜこのような設計なのか

TweenJSは、メソッドチェーンでアニメーションを柔軟かつ簡単に記述できることを特徴としています。公式サイトの[GETTING STARTED](https://www.createjs.com/getting-started/tweenjs)に掲載されているサンプルがその設計を代表しています。

```js
createjs.Tween.get(circle, { loop: true })
  .to({ x: 400 }, 1000, createjs.Ease.getPowInOut(4))
  .to({ alpha: 0, y: 175 }, 500, createjs.Ease.getPowInOut(2))
  .to({ alpha: 0, y: 225 }, 100)
  .to({ alpha: 1, y: 200 }, 500, createjs.Ease.getPowInOut(2))
  .to({ x: 100 }, 800, createjs.Ease.getPowInOut(2));
```

このような記述ができるように、メソッドチェーンの一部にできる`.pause()`関数と、アニメーションを停止する`.paused`プロパティの2つの手段が用意されています。

設計と機能からこの2つを理解すると腑に落ちるのですが、直感的には理解しにくいと思います。この記事が皆様の一助となれば幸いです。

以上、ありがとうございました。