元FLASHerで、現HTML5erないしWebGLerの荒井です。
FLASHerあるあるの
「なんでHTML5/JavaScriptには○○が無いんだ!」
に時折悩まされながらも、代替手段を模索しながら日々お仕事をしています。
で、そのあるあるのひとつに
「JavaScriptの有名なトゥイーンライブラリにはなんでserial/parallelが無いんだ!」
というのがありました。
serial / parallelって?
比較的最近のFLASHerがお世話になったであろうTween24の例だとこんな感じです。
// 1.と2.を同時に実行
Tween24.parallel(
// 1. ここは順番に実行
Tween24.serial(
Tween24.tween(box1, 0.7, Ease24._6_ExpoInOut).x(400),
Tween24.tween(box2, 0.9, Ease24._6_ExpoInOut).x(400)
),
// 2. ここは0.5秒待ってから同時に実行
Tween24.parallel(
Tween24.tween(box3, 0.7, Ease24._6_ExpoInOut).x(400),
Tween24.tween(box4, 0.9, Ease24._6_ExpoInOut).x(400)
).delay(0.5)
).play();
で、これまた色んな人がお世話になったであろうBetweenAS3の場合はこんな感じです。
※あまり使った事がなかったのと、めちゃくちゃ久々に書いたので合ってるか自信ないかも・・・
BetweenAS3.parallel(
BetweenAS3.serial(
BetweenAS3.to(box1, {x: 400}, 0.7, Expo.easeInOut),
BetweenAS3.to(box2, {x: 400}, 0.9, Expo.easeInOut)
),
BetweenAS3.delay(
BetweenAS3.parallel(
BetweenAS3.to(box3, {x: 400}, 0.7, Expo.easeInOut),
BetweenAS3.to(box4, {x: 400}, 0.9, Expo.easeInOut)
)
, 0.5)
).play();
こんな感じでserial/parallel
を組み合わせつつ、それらをまとめてdelay
でタイミング調整するのが基本スタイルです。
時系列がパッと見で分かりやすいですし、複数のトゥイーンをまとめて扱える事で個々のトゥイーンを調整した時に他のトゥイーンへの影響をなるべく少なくする工夫がありますね。
1つのトゥイーンの時間を変えたら、後のトゥイーンも全部変えなきゃ・・・みたいな事を毎回やってると開発のテンポが悪くなります。
JavaScriptでもserial/parallelしたい
serial
については.to().to()・・・
な感じのメソッドチェーンで実装されているのも含めるとAS/JSに限らず結構多いのですが、parallel
を備えてるトゥイーンライブラリってJSだとあまり見かけません。
Haxe製であればTweenXというのがあるみたいで、ちょっと試したことはないのですが、JSでも使えるとは思います。
何はともあれ、これをJSでもやりたくて、過去には自前でトゥーインライブラリ(というか非同期処理のコマンドパターンライブラリみたいなの)を作ってみたりしてたんですが、最近はGSAP(TweenMax/TimlineMax)を使いつつ、serial/parallelできるラッパーライブラリを自作して使っています。
※ちなみに補足しておくと、CSSのtransition/animationもよく使います。この辺はケースバイケースですね。
さて、「ラッパーライブラリを自作して」とか得意気に言っちゃいましたが、実際は凄くシンプルです。
var TimelinePlus = {};
// ---------- serial
TimelinePlus.seri = function() {
var i, len, tl;
tl = new TimelineMax();
len = arguments.length;
for (i=0;i<len;i++) {
tl.add(arguments[i]);
}
return tl;
}
// ---------- parallel
TimelinePlus.para = function() {
// argumentsを直接addしたら、なんかエラー出たのでコピー
var args = Array.prototype.slice.call(arguments, 0);
return new TimelineMax().add(args);
}
これだけ。
※ serial/parallel
って長ったらしいので、seri/para
に短縮してます。
seri/para
の中でTimelineMaxのインスタンスを作り、引数で受け取ったものをadd
で突っ込んでから返すだけです。
元々、TimelineMax自体がとても高機能なのでちょっと工夫するだけで済みました。
先ほどの例を書き直すとこんな感じ。
var spans = document.querySelectorAll("span.hoge");
TimelinePlus.para(
TimelinePlus.seri(
TweenMax.to(spans[0], 0.7, { x: 400, ease: Expo.easeInOut }),
TweenMax.to(spans[1], 0.9, { x: 400, ease: Expo.easeInOut })
),
TimelinePlus.para(
TweenMax.to(spans[2], 0.7, { x: 400, ease: Expo.easeInOut }),
TweenMax.to(spans[3], 0.9, { x: 400, ease: Expo.easeInOut })
).delay(0.5)
);
serial処理は元からあるTimelineMaxの.to()
などを繋いでいくスタイルでも大丈夫ですが、seri/para
で統一しておくとそれらの切り替えがサクッとできて便利なんですよね。
一応、CodePenにもサンプルを置いてあります。
もし良かったら参考にしてみてください。
注意点
seri/para
にTweenMax.set()
を突っ込む時はimmediateRender
をfalse
にしておかないと、直ちに実行されてしまうようです。
var span = document.querySelector("span.hoge");
TimelinePlus.seri(
// immediateRenderはデフォルトのtrueでOK、一瞬チラつくのを回避
TweenMax.set(span, { x:400 }),
TweenMax.to(span, 0.7, { x: 0, ease: Expo.easeInOut }).delay(0.2)
// immediateRenderをfalseにしておけば、直ちに実行されない
// なので、上の.toが終わってから実行される
TweenMax.set(spans, { x:400, immediateRender:false }),
TweenMax.to(span, 0.7, { x: 0, ease: Expo.easeInOut }).delay(0.2)
);
この辺りはTimelineMaxの仕様に依存しているのでご注意ください。
さいごに
駆け足でしたが以上です。
こんな感じで他にも便利なメソッドをちょいちょい追加して秘伝のタレ化していくと良いと思います。
例えばですが、addClass
やremoveClass
なんてメソッドを用意しておけば、任意のタイミングでCSSのtransition/animationを発動させるのが簡単になりますよね。
それでは、良きserial/parallelライフを!