- 2009年6月 2日 01:18
- ActionScript 3.0 | JSFL

先のエントリーで、Geoff氏の誕生日用に作ったJSFLを用いたタイムラインメッセージ。
ポイントは3つ
- JSFLとAS3(SWF)の連携
- AS3によるパーティクル表現
- JSFLの限界
これを踏まえて、以下解説編です。
まず、過去に何回かご紹介しましたが、JSFLはSWFと通信が出来ます。イメージ的にはExternalInterfaceが近く、JSFLで定義されている関数の実行や、逆にAS3で定義されている関数をJSFLが呼び出すことも出来ます。
これらのメリットで大事なことは、"得意なことは得意な方にやらせる"ことができることです。もともと、JSFLは表現をする言語ではありません。そのためできることが非常に限られています。たとえばイメージのように文字の形をタイムラインに浮き出させるというのは、JSFLだけでは不可能に近いです。考え得る方法としては、決まった値をしくしくと打ち込んでいく非常に効率の悪い方法しかないでしょう。逆に、AS3にとって表現はまさに本領を発揮するところです。今回のサンプルでも、AS3で表現に関する実装を行い、それによってもたらされる値をJSFLに渡す、という方法をとっています。
手順としてはこうです。まず、前提条件として1px = 1フレームとして考え、座標系は左上とします。
- AS3で文字を書く(TextFieldの配置)
- TextFieldの大きさを取得し、JSFLに渡す。
- JSFLはタイムラインに文字を表示するための最大サイズを取得、それに合った数のキーフレームを打ち込む。
- AS3は次にBitmapDataに書き込み、書き込まれたデータを取得する。
- 取得したデータを走査し、文字のピクセル座標を把握する。
- 上記からもたらされたデータをJSFLに渡して、JSFLはその値をトゥイーンとして反映する。
AS3のソースから見ていきます。
AS3では文字の表示・ビットマップデータへの書き込み・データの走査と、それに付随する値をJSFLに対して送信を行います。
下記はAS3での全ソースです。JSFLへの送信はMMExeCuteを使用しますが、とにかく使いづらいので自作クラスのMMExecute2を使っています。
このビットマップに書き込まれたデータを取得するに当たっては、getVectorを使っています。getPixelsでByteArrayを取得するよりも、若干ですが高速です。
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.ByteArray;
/**@author kamoyusuke */
public class Message extends Sprite {
public function Message():void {
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, completeHandler);
loader.load(new URLRequest("timelineMessage.xml"))
}
private function completeHandler(e:Event):void {
XML.ignoreWhitespace = true;
var xml:XML = new XML(e.target.data);
var tf:TextField = new TextField();
tf.defaultTextFormat = new TextFormat(xml.message[0].@font,Number(xml.message[0].@size));
tf.autoSize = TextFieldAutoSize.LEFT;
tf.text = xml.message[0];
MMExecute2.setDefault(MMExecute2.CONFIG_JS_URI,"VisualizeTimeLine")
MMExecute2.run("init", [Math.round(tf.height), Math.round(tf.width)]);
//trace(Math.round(tf.width),Math.round(tf.height))
var canvas:BitmapData = new BitmapData(tf.width, tf.height, false);
canvas.draw(tf);
var board:BitmapData = new BitmapData(tf.width, tf.height, false);
var bm:Bitmap = addChild(new Bitmap(board)) as Bitmap;
drawVector(canvas,board);
}
private function drawByteArray(canvas:BitmapData, board:BitmapData = null):void{
var bytes:ByteArray = canvas.getPixels(canvas.rect);
var color:uint, p:int;
var _x:int;
var _y:int = -1;
for (bytes.position = 0; bytes.position < bytes.length; color = bytes.readUnsignedInt()) {
p = bytes.position / 4
_x = p %canvas.width;
_y += !_x ? 1: 0;
if (!color) {
//board.setPixel(_x, _y,0)
MMExecute2.run("setMotionTween",[Math.round(_y),Math.round(_x)]);
}
}
}
private function drawVector(canvas:BitmapData, board:BitmapData = null):void {
var bytes:Vector. = canvas.getVector(canvas.rect);
bytes.fixed = true;
canvas.lock();
var color:uint, i:int;
var _x:int;
var _y:int = -1;
for (i; i < bytes.length; i++) {
color = bytes[i];
_x = i %canvas.width;
_y += !_x ? 1: 0;
if (!color) {
//board.setPixel(_x,_y,0)
MMExecute2.run("setMotionTween",[_y,_x]);
}
}
}
}
}
次にJSFLのソースを見ていきます。まず、MMExecute2によって、initが実行されますinitはフレームに文字を表示するために必要な高さと幅を受け取り、今開いているドキュメントのタイムラインに対して、必要な数のレイヤーと、空のキーフレームを打ち込みます。最後に、最初から生成されていたレイヤーを削除します。次に実行される関数はsetMotionTweenです。この関数はAS3がビットマップデータを走査し、有色のピクセルを見つけた時に、必要なレイヤー番号と、フレーム番号を受け取り実行されます。内部で使用しているTimeline.currentLayerプロパティは、インデックス値を指定することで選択しているレイヤーを変更することが出来ます。Timeline.createMotionTween()は、現在アクティブになっているレイヤーで、第一引数のスタートのフレーム番号と、終点のフレーム番号のフレームをモーショントゥイーンに変換します。
var currentTimeline = fl.getDocumentDOM().getTimeline();
var layerHeight;
var frameLength;
function init(_layerHeight,_frameLength){
layerHeight = _layerHeight;
frameLength = _frameLength;
for(var i= 0;i<layerHeight;i++){
var targetLayer = currentTimeline.layers[currentTimeline.addNewLayer()];
currentTimeline.convertToBlankKeyframes(0,frameLength+1)
}
currentTimeline.deleteLayer(currentTimeline.layers.length-1)
}
//指定フレームをモーショントゥイーンに設定
function setMotionTween(layerIndex,frameIndex){
currentTimeline.currentLayer = layerIndex
currentTimeline.createMotionTween(frameIndex,frameIndex)
}
このように、AS3とJSFLで役割を分け、連携を図ることでさらなる表現も可能です(かなりむりやりですが・・)。実際、CSファミリーに付属しているKulerもこのような連携を図って実装されています。とはいえ、なんでもできるというわけでもなく、もともとハードな設計はされていないため、あんまり無理しすぎると処理が停止してしまうので注意する必要があります。



