前回の記事でクロスドメインで読み込んだswfの埋め込みフォントをregisterFont出来ないと書いたが、バイナリとしてswfを読み込んでfontを取り出しているDenis Kolyako氏という方がいました。
http://etcs.ru/blog/as3/fontloader/
キリル文字で書かれてるのでサッパリ何が書いてあるのかわかりませんが、Demoページもソースも公開されていますのでなんとかなるでしょう。
前回の記事でクロスドメインで読み込んだswfの埋め込みフォントをregisterFont出来ないと書いたが、バイナリとしてswfを読み込んでfontを取り出しているDenis Kolyako氏という方がいました。
http://etcs.ru/blog/as3/fontloader/
キリル文字で書かれてるのでサッパリ何が書いてあるのかわかりませんが、Demoページもソースも公開されていますのでなんとかなるでしょう。
クロスドメインで読み込んだswfから埋め込みフォントはregisterFontできないっぽい。
Fontクラスを参照しようとすると59行目でエラーとなる。
Wonderflを試すってことで、利用可能なフォントをリストアップするサンプル。
利用可能なフォントはFont.enumerateFonts()によりFontを要素とする配列が得られる。配列には得埋め込みフォントしか含まれていないが、Font.enumerateFonts()の引数にtrueを渡すとDeviceフォントも配列に含まれるようになる。
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); } } } }
前の記事では、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なのか謎(笑)
埋め込まれたアセットがどう処理されてるのかどっかに解説ないかなぁ。
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; } } } }
フルフラッシュなサイトで、Flashコンテンツが必要とするサイズ以上にブラウザの領域が大きい場合に、Flashコンテンツを自動的にスケーリングしたい時がある。みんなどうやってるのかなっと思っていろいろリサーチ、jQueryやPrototypeなんかで処理してるひともいれば、javascriptのライブラリ利用してる人もいる模様。
javascriptのライブラリはswffit と swfforcesizeなどがある。両者ともSwfObjectと併せて使用することを前提にしている。swfforcesizeはシンプルにコンテンツサイズを最大に設定してくれるだけだが、swffitは最小サイズ、最大サイズ、リサイズ時のイベントハンドらを登録できたりとswfforcesizeよりも高機能、ドキュメントもしっかりしてる。swffitを採用しているサイトは複数あったから、結構有名なライブラリなのかな。
swffitをダウンロードすると、サンプルがいくつか含まれているので、都合のよい物をカスタマイズして使う。ただし、サンプルはFlashのセキュリティ制限のせいでローカルファイルとしてブラウザに読み込ませると、セキュリティエラーが発生する。サンプルの動作を観たい人は、swffitのページにサンプルが掲載されているのでそちらを観るといいでしょう。
記述するjavascriptは、SWFObjectによって、Flashコンテンツを利用できる状態にした後、swffit.fit()によってリサイズする機能を追加する。
swffit.fit("flashID", minWidth, minHeight, maxWidth, maxHeight, horizontalCentered, verticalCentered );
必須なのはflashID,minWidth, minHeightの3つ、あとのパラメータは省略できる。
続いて、ViewStackおよび、Viewについて、この2つのクラスは表示に関する処理をおこなうもので、通常、それぞれのクラスを継承したサブクラスで必要な処理を実装する。
ViewStackクラスのサブクラスでオーバーライドを行うメソッド
Viewクラスでオーバーライドするメソッド
sugarcookieというフレームワークが、映画007の新作Quantum Of Solace(邦題:慰めの報酬)のHPに使用されているみて興味がでたので、ちょっとメモを取りつつコード読み。
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によるアプリケーション/サイトの開発を助けてくれる物のような感じ。以下のようなものが含まれてます。
まずは、Applicationクラス。
アプリケーションはsugarcookie.framework.Applicationクラスを継承する。
Applicationを継承したクラスは、メソッドinitData、initApp、initDisplayをオーバーライドして必要な処理を行う。
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以外のファイルもデータチャンネルとして読み込むことができる。
AS3むけのYAMLライブラリがないかなと探してみたら、as3yamlというライブラリがGoogle Codeで公開されていた。
Kirill SimonovのPython向けライブラリPyYAMLをAS3に移植したものらしい。
現在どの程度の完成度なのかちょっと解らないが、yamlドキュメントのデコード・エンコードは十分できるような感じ。
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 */