Yusuke Kamo http://log.xingxx.com/

quack!quack!quack!

Writing text in the Flash Timeline using JSFL

timeline message

In my previous entry, I wrote about the Timeline message I made in JSFL for Geoff when he was here in Tokyo during our last User Group Meeting.

The 3 things I want to highlight from that are:

* How it is possible to talk between JSFL and AS3
* Rendering Particles in AS3
* Reaching the limits of JSFL

Taking that in consideration, let me try to explain in detail what it all means:

First of all, as I’ve already mentioned this, it is possible to communicate between JSFL and AS3.
It’s very close to how you use ExternalInterface, and in a similar way, you can call from AS3 methods defined in JSFL, as well as calling JSFL methods defined in AS3.

The task then becomes splitting responsibilities properly, or “letting each object do by themselves what they are really good at doing”.
Originally, JSFL was not a language thought for expression. So in a way you will very quickly find how limited it is. However, we can tweak things a little bit and try writing shapes that resemble type in the timeline by almost using only JSFL.
One of the possible methods to create something like this would be by defining a very large range of variables representing each character. But this seems to be a very inefficient method of doing things.

Opposite to that, in AS3, expression is really one it’s most inherent characteristics.

In the following example, we create all the text character variables we need in AS3 and we then hand that over to JSFL as a package so it can handle that input.

The process goes as follows:

First as a precondition, we thing of 1 pixel as 1 frame. The coordinate origin is set at the top left.

* In AS3 we measure the size of the Textfield and pass that to JSFL
* In JSFL we measure the biggest size needed to represent all text, and then create as many keyframes as needed to fit that.
* In AS3 we then copy the contents of the textfield into a BitmapData object.
* We then grab the output of that BitmapData, scan it, and grab all coordinates for each pixel we want to represent.
* Finally pass each of those coordinates to JSFL

Below is all the AS3 source.

When sending data to JSFL we make use of MMExeCute, but since its pretty awkward to use, we wrap that in our own MMExecute2.

To grab all data from our Bitmap we use getVector since it many times faster than trying to manipulate a ByteArray using getPixels.

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]);
				}
			}
		}
	}

}

Next is the source for the JSFL.

Once we call our init method in MMExecute2, we measure the height and with of the text we want to represent and we then create as many layers and blank frames we need in the document we are currently on. Finally, we delete all layers that where originally in the document. The next thing we call is setMotionTween. This method receives the bitmap data, scans it and whenever it finds a colored pixel it executes it on the set layer and frame number. The internal property being used Timeline.currentLayer sets the layer to be modified next. Timeline.createMotionTween then creates a motion tween from and to the frameIndex received.

var currentTimeline = fl.getDocumentDOM().getTimeline();
var layerHeight;
var frameLength;

function init(_layerHeight,_frameLength){
	layerHeight = _layerHeight;
	frameLength =  _frameLength;
	for(var i= 0;i$lt;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)
}

So as you can see, it is possible to very easily cooperate between the two worlds and this opens the possibility to many more ideas.

This is for example how Kuler actually integrates with the Adobe CS family products.
That said, since this is a method that has not been really designed for this sort of interaction, it is very possible that things will simply break when trying to push it too hard, so be advised!

---------------------------------------------------------------------------

Thanks to him, I was able to open to the public.
Against Mr. Marcos, and I am deeply grateful !!

This is Yusuke Kamo

Hi World!

Please let me introduce myself…

I’m Yusuke Kamo a.k.a Kaede.I’m FlashDeveloper& TechnicalWriter in rokunana.The name of my company means 67 in Japanese. Moreover, my family name is a meaning of drake in Japanese.quack ! quack! quack! :)

And, perhaps…

I’ll write my next entry about JSFL.

Don’t miss it!

log :: xingxx.com,twitter ::@KaedeAS