前回から、TweenJSのデモ「TWEEN CIRCLES」をお題にした。同心円のリングが、ステージ上のクリックした位置に、時間差でトゥイーンする。前回、一応それらしい動きはできた。けれども、Canvasのあちこちを続けざまにクリックすると、同心円にしたリングのトゥイーンがばらばらになってしまった(図1)。今回は、アニメーションやインタラクションをよく確かめて、スクリプトの仕上げにかかる。
隊列を乱さない
ステージ上を素早くあちこちクリックすると、なぜリングのトゥイーンは隊列を乱してしまうのか。それは、先頭と後尾でトゥイーンを時間的にずらしているからだ。先頭は前のトゥイーンをすでに終えていて、すぐにつぎのトゥイーンに向かう。しかし、最後尾はまだ前のトゥイーンが終わっていないため、つぎの目的地には向かえず、今のトゥイーンを終えようとする。だから、それぞれの向かう先がばらばらになってしまうのだ。
これを避けるには、新しいトゥイーンを命じるときには、前のトゥイーンは途中であっても止めさせればいい。そのメソッドが、静的メソッドTween.removeTweens()だ。引数には対象となるインスタンスを渡す。すると、そのインスタンスに定められたすべてのトゥイーンは解除される。
前回のコード2のトゥイーンを定める関数には、つぎのようなTween.removeTweens()メソッドの呼出しを加えればよい。
もっとも、新しいトゥイーンを命じたら、前のトゥイーンはつねに無視してよいなら、Tween.get()メソッドの第2引数でも定められる。今回のお題なら、こちらの方が簡単だ。
第2引数はObjectインスタンスで渡す。オブジェクトには、設定したいプロパティとその値を納める。新たなトゥイーンを加えるとき前のトゥイーンはすべて除いてしまいたいときは、overrideプロパティにtrueを定めればよい(デフォルト値はfalse)。
これで、ステージを素早く続けざまにクリックしても、古いトゥイーンはすべてのリングから除かれるため、全体が新たな目的地に向かってトゥイーンを始める。もはや隊列の乱れはない(図2)。まだ修正はわずかに引数ひとつではあるが、前回までのおさらいもかねて、つぎのコード1にscript要素全体を掲げる。
無駄遣いはしない
動きとしては、これでよさそうだ。しかし、仕上げに当たっては、他に手をいれるべきことはないか考えたい。参考にしたTweenJSのデモ「TWEEN CIRCLES」のコードを見ると、つぎのような処理が加わっている。何をしているのかというと、無駄な画面の描き替えをなくそうというのだ。
前掲コード1では、Ticker.addEventListener()メソッドでTicker.tickイベントのリスナーにStageオブジェクトを加えた。そうすれば、Stage.update()メソッドを呼出すだけのリスナー関数は書かなくて済む。ただし、誰もクリックせず、アニメーションがなくても、画面は描き続けられる。それは無駄だろうという発想には一理ある。
この処理を加えたJavaScriptコード全体は、後にコード2として掲げた。まず、Ticker.addEventListener()メソッドで加えるTicker.tickイベントのリスナーには、Stageオブジェクトに替えて新たな関数(tick())を定めた。そして、初期化の関数(initialize())とマウスクリックのリスナー関数(startTween())がリングのトゥイーンをforループで定めたすぐ後、新たに加えた変数(activeCount)にリングの総数(circleCount)を与えている。
つぎに、トゥイーンを定める関数(setTween())には、Tween.call()メソッドの呼出しが加わった、引数に渡した関数(tweenComplete())は、リングのトゥイーンがひとつ終わるたびに呼出される。そして、その関数は、新たに定めた変数(activeCount)の値をカウントダウン(デクリメント--)していく。つまり、この変数は、まだトゥイーンが続いているリングの個数を示す。
そこで、Ticker.tickイベントのリスナー関数(tick())は、if条件でまだトゥイーンしているリングの残り個数(activeCount)があるときだけStage.update()メソッドを呼出す。これで、無駄な画面の書き替えはなくなりそうだ。
このトゥイーンアニメーションを何度か試しながらよく見てみると、気になるところがある。初め(initialize())のランダムな位置から中央へのトゥイーンで、つぎのようにランダムな座標(nX)の幅を極端に拡げてみるとはっきりする。トゥイーンする最後のリングが、目的の中心までたどり着かずに力つきてしまう(図3)。
帰るまでが遠足
前掲コード2で最後のリングが目的地にたどりつけないのは、処理の詰めに問題がある。家に帰るまでが遠足なのだ。すべてのリングのトゥイーンを終えて、残り個数がなくなったら画面は書替えないという考え方はよい。しかし0になったとき、最後にたどり着いたリングは描画すべきだ。それをしていないために、最後のリングが卒業写真に欠席した子のようになってしまった。
最後にゴールする子まできちんと見届けようとするなら、つぎのような手を加えればよいだろう。残り個数が0になったときは、1度画面は描き替えてカウンタの変数(activeCount)を減らしている。そして、変数がマイナスになったら、もはや再描画はしない。
ただこの処理は、後からつけ足した感が否めない。それに、画面の描き替えは省けても、Ticker.tickイベントは続く。イベントリスナーを除いてしまえば、イベントそのものが止められる。つぎのように、スクリプトももう少しすっきりする。
さらに見直せば、まだ手を入れる余地はあるだろう。だが、このお題については、style要素につぎのようなCanvasの背景色を加えて結びとしたい(図4)。上述とおりにイベントリスナーの削除を加えたJavaScriptコード全体は、つぎにコード3として掲げる。あわせて、jsdo.itにコードに公開したコードも添える。次回は、また新たなお題に取り組もう。