12 月 30

前回の記事でクロスドメインで読み込んだswfの埋め込みフォントをregisterFont出来ないと書いたが、バイナリとしてswfを読み込んでfontを取り出しているDenis Kolyako氏という方がいました。

http://etcs.ru/blog/as3/fontloader/

キリル文字で書かれてるのでサッパリ何が書いてあるのかわかりませんが、Demoページもソースも公開されていますのでなんとかなるでしょう。

12 月 29

クロスドメインで読み込んだswfから埋め込みフォントはregisterFontできないっぽい。
Fontクラスを参照しようとすると59行目でエラーとなる。

12 月 28

Wonderflを試すってことで、利用可能なフォントをリストアップするサンプル。
利用可能なフォントはFont.enumerateFonts()によりFontを要素とする配列が得られる。配列には得埋め込みフォントしか含まれていないが、Font.enumerateFonts()の引数にtrueを渡すとDeviceフォントも配列に含まれるようになる。

12 月 25

MovieClipLoaderAssetのソースを読んだら、コンストラクタでLoaderのloadBytesを使って埋め込みアセットを読み込んでるだけだった。前の記事での

 
Loader(_mov.getChildAt(0)).contentLoaderInfo.addEventListener(Event.INIT, initHandler);
 

は、このLoaderを参照してるわけですな。

 
//MovieClipLoaderAssetコンストラクタ
public function MovieClipLoaderAsset()
	{
		super();
 
		var loaderContext:LoaderContext = new LoaderContext();
		loaderContext.applicationDomain = new
			ApplicationDomain(ApplicationDomain.currentDomain);
 
		loader = new Loader();
		loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
												  completeHandler);
		loader.loadBytes(movieClipData, loaderContext);
		addChild(loader);
	}
 
 
ApplicationDomain(ApplicationDomain.currentDomain);

とあるように、クラスはカレントドメインの子ドメインに読み込まれる。

結局、アプリケーションのルートのApplicationDomainにクラスを読み込んで使うにはLoaderクラスのloadBytesにloaderContextでApplicationDomainを指定してやってっと
、ちょっと手間がかかる感じですね。
Frameメタデータタグを利用すれば、ちょっとはスマートか?
以下のような感じで。

 
package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.system.ApplicationDomain;
	import mtok.sample.ProgressBar;
 
	/**
	 * ...
	 * @author DefaultUser (Tools -> Custom Arguments...)
	 */
	[Frame(factoryClass="mtok.assets.AssetsLoader")]
	public class AssetPreloading extends Sprite
	{
		public function AssetPreloading()
		{
			addChild( new ProgressBar());
			//ちゃんとアセットで読み込んだ物が表示される。
		}
	}
}
 
 
package mtok.assets
{
	import flash.display.DisplayObject;
	import flash.display.LoaderInfo;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.system.LoaderContext;
	import flash.system.ApplicationDomain;
	import flash.utils.getDefinitionByName;
	/**
	 * ...
	 * @author DefaultUser (Tools -> Custom Arguments...)
	 */
	public class AssetsLoader extends MovieClip
	{
		[Embed(source = "../../my_progressbar.swf", mimeType = "application/octet-stream")]
		private var _clsAssets:Class;
		private var _appName:String = "AssetPreloading";
		public function AssetsLoader()
		{
			var context:LoaderContext = new LoaderContext();
			context.applicationDomain = ApplicationDomain.currentDomain;
			var l:Loader = new Loader();
			l.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
			l.loadBytes(new _clsAssets(), context);
			stop();
		}
 
		private function completeHandler(e:Event):void
		{
			var li:LoaderInfo = e.target as LoaderInfo;
			nextFrame();
			startup();
		}
 
		private function startup():void
		{
			trace('startup');
			var mainClass:Class = Class(getDefinitionByName(_appName));
			if (mainClass) {
				addChildAt( new mainClass() as DisplayObject, 0);
			}
		}
	}
}
 
12 月 24

前の記事では、LoaderクラスのloadBytesを使ってアセットを読み込んだが、次のような書き方もできるようです。

 
package
{
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.MovieClip;
	import flash.events.Event;
 
	import mx.core.MovieClipAsset;
 
	public class AllowDomainSample extends MovieClip
	{
		[Embed(source="../assets/preloader.swf")]
		private var _embedAssets:Class;
		private var _mov:MovieClipAsset;
		private var _flag:Boolean = false;
		public function AllowDomainSample()
		{
			trace('constructor', _flag);
			super();
			_mov = new _embedAssets() as MovieClipAsset;
			Loader(_mov.getChildAt(0)).contentLoaderInfo.addEventListener(Event.INIT, initHandler);
 
		}
 
		private function initHandler(event:Event):void{
			trace('init');
			var l:LoaderInfo = event.target as LoaderInfo;
			var cls:Class = l.content.loaderInfo.applicationDomain.getDefinition("mtok.preloader.MtokPreloader") as Class;
			addChild(new cls());
		}
	}
}
 

埋め込んだアセットのMovieClipAsset(もしくはStripAsset)に対してgetChildAt(0)して、さらにそれをLoaderにキャストして、contentLoaderInfoにアクセスしているわけですが、なんでこれでOKなのか謎(笑)
埋め込まれたアセットがどう処理されてるのかどっかに解説ないかなぁ。

12 月 19

Embedメタデータタグをつかってswfをアセットとして埋め込んだ場合、MoviceClipAssetクラスとして扱われる。ここによると

埋め込んだ SWF ファイルのプロパティまたはメソッドに直接アクセスすることはできません。 ただし、 LocalConnection を使用してアクセスすることはできます。

なんてことが書いてあるので、Flashで書きだしたswfのクラスにアクセスするにはembedは使えないなんて思ってました。しかし、skinner氏がUsing Flash Symbols with Actionscript in Flexという記事で"Shake'n'Bake SWFs"と名付けた方法でEmbedメタデータタグで埋め込んだSwfからクラス定義を取り出す方法を紹介している。

なーるほどっ、バイナリとして埋め込んでloadBytesで読み込むわけですね。
 アセットの埋め込みについてに記載があるとおり、mimetypeにapplication/octet-streamの指定を行えばバイナリとして埋め込まれるようです。
この方法、結構、定石なんですかね。

備考としては、loadBytesでロードしたswfはセキュリティドメインに読み込まれるので,
AIRの場合にセキュリティ的にアカン(Ted Patrick: Loader.load vs Loader.loadBytew)ってことになったので、実行可能なコードを読み込む場合はloadBytesにallowLoadBytesCodeExecutionをtrueに設定したLoaderContextを渡す必要があります。

 

サンプル

 
package
{
	import flash.display.Loader;
	import flash.display.LoaderInfo;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.system.LoaderContext;
	import mtok.sample.ProgressBar;
	/**
	 * ...
	 * @author DefaultUser (Tools -> Custom Arguments...)
	 */
	public class LoadBytesSample extends MovieClip
	{
		[Embed(source = "my_progressbar.swf", mimeType = "application/octet-stream")]
		private var _embAssets:Class;
		private var _bar:ProgressBar;
		public function LoadBytesSample()
		{
			super();
			addEventListener(Event.ADDED_TO_STAGE, addedToStageHanlder);
		}
 
		private function addedToStageHanlder(e:Event):void
		{
			/*
			 * loadBytesしてもすぐには使用できない。
			 * 1フレーム待つか、Event.COMPLETEイベントを利用する。
			 */
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHanlder);
			loader.loadBytes(new _embAssets());
		}
 
		private function completeHanlder(e:Event):void
		{
			var cls:Class;
			var bar:ProgressBar;
			var li:LoaderInfo = e.target as LoaderInfo;
			if (li.applicationDomain.hasDefinition('mtok.sample.ProgressBar')) {
				cls = li.applicationDomain.getDefinition('mtok.sample.ProgressBar') as Class;
				_bar = new cls() as ProgressBar;
				addChild(_bar);
				_bar.setPercent(0.5);
				_bar.x = (800  - _bar.width) * 0.5;
				_bar.y = (600 - _bar.height) * 0.5;
			}
		}
	}
}
 

12 月 12

フルフラッシュなサイトで、Flashコンテンツが必要とするサイズ以上にブラウザの領域が大きい場合に、Flashコンテンツを自動的にスケーリングしたい時がある。みんなどうやってるのかなっと思っていろいろリサーチ、jQueryやPrototypeなんかで処理してるひともいれば、javascriptのライブラリ利用してる人もいる模様。

javascriptのライブラリはswffitswfforcesizeなどがある。両者ともSwfObjectと併せて使用することを前提にしている。swfforcesizeはシンプルにコンテンツサイズを最大に設定してくれるだけだが、swffitは最小サイズ、最大サイズ、リサイズ時のイベントハンドらを登録できたりとswfforcesizeよりも高機能、ドキュメントもしっかりしてる。swffitを採用しているサイトは複数あったから、結構有名なライブラリなのかな。

swffitの使いかた

swffitをダウンロードすると、サンプルがいくつか含まれているので、都合のよい物をカスタマイズして使う。ただし、サンプルはFlashのセキュリティ制限のせいでローカルファイルとしてブラウザに読み込ませると、セキュリティエラーが発生する。サンプルの動作を観たい人は、swffitのページにサンプルが掲載されているのでそちらを観るといいでしょう。
記述するjavascriptは、SWFObjectによって、Flashコンテンツを利用できる状態にした後、swffit.fit()によってリサイズする機能を追加する。

swffit.fit("flashID", minWidth, minHeight, maxWidth, maxHeight, horizontalCentered, verticalCentered );

必須なのはflashID,minWidth, minHeightの3つ、あとのパラメータは省略できる。

12 月 10

続いて、ViewStackおよび、Viewについて、この2つのクラスは表示に関する処理をおこなうもので、通常、それぞれのクラスを継承したサブクラスで必要な処理を実装する。

ViewStackクラスのサブクラスでオーバーライドを行うメソッド

init()
コンストラクタ内で呼び出される、ここでViewクラスのインスタンスをregisterViewで追加してやる。
onAdded()
ステージに追加されたときに呼び出される
queueIntro()
config.xmlでhasIntroがtrueの場合、イントロが存在するとみなされて、queueIntro()が呼び出される。queueIntro()ではイントロを開始する処理を行う。イントロ終了後はcontinueAfterIntro()を呼び出してイントロ終了後の処理を開始する。initDisplay()はそのままだと呼び出されないので必要があれば、initDisplay()を呼び出す。
initDisplay()
おまかせ。
preTransitionHandler()
SWFAddressのonChange発生じに呼び出され、トランジションが始まる前
postTransitionHandler
トランジションが終了してViewが表示された後
makeTrackingCall
トラッキングコール
onProgressHandler()
必要に応じて
onProgressComplete
必要に応じて

Viewクラスでオーバーライドするメソッド

onShow()
Viewを表示させるトランジションをスタート。トランジション終了時にonShowComplete()を呼ぶ
onHide()
Viewを非表示にするトランジションをスタート。トランジション終了時にonHideComplete()を呼ぶ
reset()
Viewをリセット。他のViewが表示されるときに呼び出される。
onAdded()
ステージに追加されたときに呼び出される
12 月 10

sugarcookieというフレームワークが、映画007の新作Quantum Of Solace(邦題:慰めの報酬)のHPに使用されているみて興味がでたので、ちょっとメモを取りつつコード読み。

SVNからソースを入手する

SVNからソースをダウンロードする。ディレクトリ構造は以下の通り。(Rev.152)

sugarcookie
├─ant_scripts
│  ├─docs
│  │  └─asdoc-output
│  └─sugarcookie
├─as2
│  └─sugarcookie
│      ├─adapters
│      ├─browser
│      ├─collections
│      ├─dataHandlers
│      ├─encryption
│      ├─events
│      ├─file
│      ├─framework
│      │  └─vo
│      ├─logger
│      ├─number
│      ├─tracking
│      ├─tween
│      ├─ui
│      ├─utils
│      ├─validators
│      └─video
├─as3
│  └─sugarcookie-framework
│      ├─sugarcookie
│      │  ├─adapters
│      │  ├─browser
│      │  ├─collections
│      │  ├─data
│      │  │  ├─database
│      │  │  └─handlers
│      │  │      ├─config
│      │  │      ├─flex
│      │  │      │  └─remoting
│      │  │      │      └─events
│      │  │      ├─remoting
│      │  │      ├─vo
│      │  │      └─xml
│      │  ├─effects
│      │  ├─error
│      │  ├─events
│      │  ├─file
│      │  │  └─vo
│      │  ├─framework
│      │  │  └─vo
│      │  ├─logger
│      │  ├─media
│      │  ├─preloaders
│      │  │  └─flex
│      │  ├─style
│      │  │  └─vo
│      │  ├─text
│      │  ├─tween
│      │  ├─utils
│      │  └─validators
│      └─swc
├─docs
│  ├─flexunit-0.9
│  │  ├─flexunit
│  │  │  ├─flexui
│  │  │  │  ├─controls
│  │  │  │  │  ├─left
│  │  │  │  │  │  ├─events
│  │  │  │  │  │  └─itemRenderer
│  │  │  │  │  └─right
│  │  │  │  ├─data
│  │  │  │  │  └─filter
│  │  │  │  ├─event
│  │  │  │  └─patterns
│  │  │  ├─framework
│  │  │  ├─runner
│  │  │  ├─textui
│  │  │  └─utils
│  │  └─images
│  ├─sugaarcookie-0.4
│  │  ├─ant-scripts
│  │  ├─quickstart
│  │  ├─sugarcookie-framework
│  │  │  ├─images
│  │  │  └─sugarcookie
│  │  │      ├─adapters
│  │  │      ├─browser
│  │  │      ├─collections
│  │  │      ├─data
│  │  │      │  ├─database
│  │  │      │  └─handlers
│  │  │      │      ├─config
│  │  │      │      ├─flex
│  │  │      │      │  └─remoting
│  │  │      │      │      └─events
│  │  │      │      ├─remoting
│  │  │      │      ├─vo
│  │  │      │      └─xml
│  │  │      ├─effects
│  │  │      ├─error
│  │  │      ├─events
│  │  │      ├─file
│  │  │      │  └─vo
│  │  │      ├─framework
│  │  │      │  └─vo
│  │  │      ├─logger
│  │  │      ├─media
│  │  │      ├─preloaders
│  │  │      │  └─flex
│  │  │      ├─style
│  │  │      │  └─vo
│  │  │      ├─text
│  │  │      ├─tween
│  │  │      ├─utils
│  │  │      └─validators
│  │  └─xml
│  ├─swfaddress-2.1
│  │  ├─de
│  │  │  ├─as
│  │  │  └─js
│  │  ├─en
│  │  │  ├─as
│  │  │  └─js
│  │  ├─it
│  │  │  ├─as
│  │  │  └─js
│  │  └─pt
│  │      ├─as
│  │      └─js
│  └─tweener-1.31.71
│      └─html
│          └─en-us
│              ├─images
│              ├─js
│              ├─methods
│              ├─misc
│              ├─parameters
│              └─properties
├─js
│  ├─asual
│  ├─sugarcookie
│  └─swfobject
├─lib
│  ├─caurina
│  │  └─transitions
│  │      └─properties
│  ├─com
│  │  └─asual
│  │      └─swfaddress
│  ├─dannypatterson
│  │  └─remoting
│  └─flexunit
├─logger
│  └─LumberJack
│      ├─.settings
│      ├─bin
│      └─html-template
├─quickstart
│  ├─bin
│  │  ├─css
│  │  ├─global
│  │  ├─js
│  │  └─xml
│  ├─css
│  ├─embed
│  │  └─fonts
│  ├─html-template
│  ├─quickstart
│  │  ├─data
│  │  ├─preloader
│  │  ├─styles
│  │  ├─ui
│  │  └─views
│  ├─xhtml
│  │  └─global
│  └─xml
└─xml
    └─sugarcookie-config
        └─DTD

ざっと見た感じ、SWFObject,SWFAddress,Tweener,LumberJack等のActionscript/Javascriptライブラリとそれらを連携させるsugarcookie frameworkによるツールキットで、Flexによるアプリケーション/サイトの開発を助けてくれる物のような感じ。以下のようなものが含まれてます。

ant_script
AsDoc、SWC書き出しのbuild.xmlファイル
as2
AS2向けのライブラリ
as3
AS3向けのライブラリ
docs
AsDocドキュメント
js
SWFAddress,SWFObject,SugarcookieのJavascriptライブラリ
lib
Tweener, SWFAddress, FlexUnit, Danny Patterson氏の AS3 Lightweight Remoting Framework のActionscriptライブラリ
logger
Lumber Jackというjavascriptロガー
quickstart
サンプルアプリケーション
xml
sugarcookieの設定ファイルconfig.xmlとそのDTD

サンプルquickstartを読む

まずは、Applicationクラス。
アプリケーションはsugarcookie.framework.Applicationクラスを継承する。
Applicationを継承したクラスは、メソッドinitData、initApp、initDisplayをオーバーライドして必要な処理を行う。

  1. Frameメタタグでプリローダーを設定する。
  2. プリローダは、sugarcookie.preloaders.AppLoaderクラスを継承する
  3. initDataではDataManager、もしくはDataManagerのサブクラスのインスタンスを_dataManagerに設定する。
  4. initApp()は、DataManagerの初期化が終わったあとに実行される。
  5. initApp()では、AppLoader.APPLICATION_READYイベントを発行する。APPLICATION_READYイベントによってプリローダが非表示され,ApplicationクラスのinitDisplay()が呼び出される。
  6. initDisplay()ではViewStackのサブクラスを生成し、ApplicationインスタンスにaddChild()してディスプレイリストに追加する。
package {
	import quickstart.DisplayManager;
	import quickstart.data.QSData;
	import quickstart.styles.QSStyles;

	import sugarcookie.events.GenericEvent;
	import sugarcookie.framework.Application;
	import sugarcookie.preloaders.AppLoader;

	[Frame(factoryClass="quickstart.preloader.QuickstartLoader")]
	[SWF(backgroundColor="#000000", frameRate="30")]
	public class SugarcookieQuickstart extends Application {
		private var _styles:QSStyles;

		public function SugarcookieQuickstart() {
			super();
		}

		override protected function initData():void {
			//GenericEvent.LOG_DATA = false;
			_dataManager = QSData.getInst(_configUrl,_initDeepLink);
		}

		override protected function initApp():void {
			_styles = new QSStyles();

			dispatchEvent(new GenericEvent(AppLoader.APPLICATION_READY,true,false,true,true));
		}

		override public function initDisplay():void {
			_displayManager = new DisplayManager();
			addChild(_displayManager);
		}

		override protected function trackingHandler(evt:GenericEvent):void {

		}
	}
}

DataManagerクラスは、アプリケーションの設定ファイルconfig.xmlをデータチャンネルとしてもち、その値へのアクセスを提供する。config.xmlのautoLoadXML要素を使用すれば、config.xml以外のファイルもデータチャンネルとして読み込むことができる。

11 月 27

AS3むけのYAMLライブラリがないかなと探してみたら、as3yamlというライブラリがGoogle Codeで公開されていた。
Kirill SimonovのPython向けライブラリPyYAMLをAS3に移植したものらしい。
現在どの程度の完成度なのかちょっと解らないが、yamlドキュメントのデコード・エンコードは十分できるような感じ。

as3yaml - Google Code

デコード

YAMLをデコードしてするには、decode()もしくは、load()メソッドを使用する。
decode()にYAML文字列を渡すと、パースされたオブジェクトが得られる。load()メソッドはdecode()メソッドに加えてより細かい設定が可能。
デコードによって得られるオブジェクトの型はデコードするYAMLによって変わり、DictionaryかArrayのどちらかが生成される。

 
	var obj:Object;
	var s:String = new String(
	"Profile:\n" +
	"  Name: Taro\n" +
	"  Age: 10\n" +
	"  City: Tokyo\n" +
	"  Zip: 101-1234\n" +
	"  Friends:\n" +
	"    - Jiro\n" +
	"    - Goro\n"
	);
	obj = YAML.load(s, new DefaultYAMLFactory(), new DefaultYAMLConfig());
 
	trace(obj.Profile.Name); 		//Taro
	trace(obj.Profile.Friends[0]); //Jiro
 

エンコード

エンコードはencode()もしくはdump()メソッドにより行う。
Object,Array,HashMapをエンコードしてくれる。
dump()メソッドに渡すYAMLConfigを使用すれば、インデントの幅等を設定できる。

 
	var obj:Object = new Object();
	obj.name = "Matzmtok";
	obj.age = 30;
	var str:String = YAML.encode(obj);
 
	trace(str);
	/* result
	--- !actionscript/object:Object
	age: !!float 30
	name: Matzmtok
	*/
 
	var map:HashMap = new HashMap();
	map.put("firstname", "foo");
	map.put("familyname", "bar");
	map.put("age", 3);
	trace(YAML.encode(map));
	/*
	---
	firstname: foo
	familyname: bar
	age: !!float 3
	 */
	var ary:Array = [3.0, int(10), "hogehoge"];
	trace(YAML.encode(ary));
	/*
	---
	- !!float 3
	- !!float 10
	- hogehoge
	*/
 
	var records:Map = new HashMap();
	var record:Map = new HashMap();
	records.put("one", record);
	record.put("firstname", "fooooo");
	record.put("familyname", "barrr");
	record.put("list", [1, 2, 3, 4]);
 
	trace(YAML.encode(records));
	/*
	one:
	firstname: fooooo
	familyname: barrr
	list:
		- !!float 1
		- !!float 2
		- !!float 3
		- !!float 4
	 */