TLFTextFieldを弄っていて気づいたところ。
デフォルトで隙間があるんです。TextFieldにもあったけど。
borderをつけてみるとわかる。
XML.prettyPrinting = false;
XML.prettyIndent = 0;
var markup:XML = <TextFlow xmlns="http://ns.adobe.com/textLayout/2008"><p><img source="contrail.jpg" width="200" height="150"/></p></TextFlow>;
var field:TLFTextField = new TLFTextField();
field.border= true;
field.x = 10;
field.y = 10;
field.width = 230;
field.height= 200;
field.tlfMarkup = markup.toString();
addChild( field );
端から2pxの隙間がある。

この隙間を無くしたいと思いました。
paddingだろうと思って、それらしきものを調べてみるとすべて未定義。
//結果はもれなくundefined。
trace( field.textFlow.paddingTop );
trace( field.textFlow.paddingLeft );
trace( field.textFlow.hostFormat.paddingTop );
trace( field.textFlow.hostFormat.paddingLeft );
trace( field.textFlow.format.paddingTop );
trace( field.textFlow.format.paddingLeft );
この隙間はコンテナレベルのpaddingに指定されていました。
//コンテナレベルのレイアウトを確認してみる。
var controller:ContainerController = field.textFlow.flowComposer.getControllerAt( 0 );
//TLFContainerController。謎のクラス。
trace( controller );
//これだ。
trace( controller.paddingTop );
trace( controller.paddingLeft );
trace( controller.format.paddingTop );
trace( controller.format.paddingLeft );
controller.paddingTop = controller.paddingLeft = 0;
field.textFlow.flowComposer.updateAllControllers();
TLFContainerControllerって名前で意味はわかりますが、ググっても全然出てこない謎のコントローラー。
やたらとテキスト周りのエントリーが続いています。
覚えること多すぎなText Layout Framework。そしてFlash Text Engine。
TLF Markupのimgタグについて気づいたことをメモ。
TLF Markupでは、InlineGraphicElementをimgタグで記述しますが、そのソース画像の指定はsource属性です。srcでないところに注意。
たとえばこの場合、画像は表示されない。
var markup:XML = <TextFlow xmlns="http://ns.adobe.com/textLayout/2008"><p><img src="contrail.jpg" width="200" height="150"/><span>飛行機雲</span></p></TextFlow>;
var flow:TextFlow = TextConverter.importToFlow( markup, TextConverter.TEXT_LAYOUT_FORMAT );
flow.flowComposer.addController( new ContainerController( container, 480, 480 ) );
flow.flowComposer.updateAllControllers();
<img src="contrail.jpg" width="200" height="150"/>
のsrcがNGなわけです。
試しにこの時のInlineGraphicElementの属性を確認してみる。
var img:InlineGraphicElement = flow.getFirstLeaf() as InlineGraphicElement;
trace( "img.source = " + img.source );
結果はnull。
とsource属性に値が無いです。
少し混乱するのがここからで、TextFlowを再度テキストに書き出してみると、
var str:String = TextConverter.export( flow, TextConverter.TEXT_LAYOUT_FORMAT, ConversionType.STRING_TYPE ) as String;
trace( str );
結果は、
<TextFlow columnCount="inherit" columnGap="inherit" columnWidth="inherit" lineBreak="inherit"
paddingBottom="inherit" paddingLeft="inherit" paddingRight="inherit" paddingTop="inherit" verticalAlign="inherit"
whiteSpaceCollapse="preserve" xmlns="http://ns.adobe.com/textLayout/2008">
<p>
<img src="contrail.jpg" height="150" width="200"/>
<span>飛行機雲</span>
</p>
</TextFlow>
src属性はそのまま残っていたりします。マークアップは別途保存されるんですかね。
これはいいとして、以前のエントリーで2段階で変換すればうまくいくと書きました。
var markup:XML = <TextFlow xmlns="http://ns.adobe.com/textLayout/2008"><p><img src="contrail.jpg" width="200" height="150"/><span>飛行機雲</span></p></TextFlow>;
var flow:TextFlow = TextConverter.importToFlow( markup, TextConverter.TEXT_FIELD_HTML_FORMAT );
markup = TextConverter.export( flow, TextConverter.TEXT_LAYOUT_FORMAT, ConversionType.XML_TYPE ) as XML;
flow = TextConverter.importToFlow( markup, TextConverter.TEXT_LAYOUT_FORMAT );
flow.flowComposer.addController( new ContainerController( container, 480, 480 ) );
flow.flowComposer.updateAllControllers();
これなら期待どおり変換されて画像が表示されます。
もう少しだけ、よさそうな方法がありました。
TextConverter.getImporterとTextConverter.getExporterを使ってまとめておくと便利かもしれません。
var HTMLImporter:ITextImporter = TextConverter.getImporter( TextConverter.TEXT_FIELD_HTML_FORMAT );
var TLFImporter:ITextImporter = TextConverter.getImporter( TextConverter.TEXT_LAYOUT_FORMAT );
var TLFExporter:ITextExporter = TextConverter.getExporter( TextConverter.TEXT_LAYOUT_FORMAT );
function getFlow( source:* ):TextFlow{
return TLFImporter.importToFlow( TLFExporter.export( HTMLImporter.importToFlow( source ), ConversionType.STRING_TYPE ) );
}
var container:Sprite= new Sprite();
addChild( container );
var markup:XML = <TextFlow xmlns="http://ns.adobe.com/textLayout/2008"><p><img src="contrail.jpg" width="200" height="150"/><span>飛行機雲</span></p></TextFlow>;
var flow:TextFlow = getFlow( markup );
flow.flowComposer.addController( new ContainerController( container, 480, 480 ) );
flow.flowComposer.updateAllControllers();
まとめのサンプル
Progression 4 で追加されたコマンドにDoExecutorがあります。
このコマンドはその名の通り指定したExecutorObjectを実行出来るのですが、便利そうなケースがあったので記録しておきます。
- トップページは、直下に子シーンを持つ。
- 子シーンは背景イメージを変更する。
- トップページでは子シーンのいずれかの背景イメージを表示する。
ぎゅっと簡略した形ですがギャラリーコンテンツなんかでありそうなケース。
子シーンはこんな感じです。シーンに到達したら背景イメージを追加するだけ。
package{
import flash.display.Bitmap;
import jp.progression.commands.Prop;
import jp.progression.commands.display.*;
import jp.progression.scenes.SceneObject;
public class ChildScene extends SceneObject{
public function ChildScene(name:String=null, initObject:Object=null){
super(name, initObject);
}
public var bmp:Bitmap; //背景イメージ
protected override function atSceneLoad():void{
//※確認用、シーンパスを表示
var indexScene:IndexScene = parent as IndexScene;
addCommand(
new Prop( indexScene.field, { text:sceneId.path } )
);
}
//背景イメージ追加
protected override function atSceneInit():void{
addCommand(
new AddChildAt( container, bmp, 0 )
);
}
//背景イメージ削除
protected override function atSceneUnload():void{
addCommand(
new RemoveChild( container, bmp )
);
}
}
}
トップページはこちら。トップページでは背景イメージをランダムに選んで表示したいのですが、背景イメージの表示は子シーンにも記述があります。なのでこれをDoExecutorコマンドで動かしてしまおうという魂胆。
package{
import flash.display.Bitmap;
import flash.text.*;
import jp.nipx.debug.btn.Btn;
import jp.progression.casts.*;
import jp.progression.commands.*;
import jp.progression.commands.display.*;
import jp.progression.commands.lists.*;
import jp.progression.commands.managers.*;
import jp.progression.commands.media.*;
import jp.progression.commands.net.*;
import jp.progression.commands.tweens.*;
import jp.progression.data.*;
import jp.progression.events.*;
import jp.progression.executors.*;
import jp.progression.scenes.*;
public class IndexScene extends SceneObject{
public function IndexScene(){
var btn:Btn;
btn = new Btn( { label:"Index", sceneId:new SceneId( "/index" ), x:5, y:225 } );
container.addChild( btn );
btn = new Btn( { label:"1", sceneId:new SceneId( "/index/1" ), x:btn.x + 5 + btn.width, y:225 } );
container.addChild( btn );
btn = new Btn( { label:"2", sceneId:new SceneId( "/index/2" ), x:btn.x + 5 + btn.width, y:225 } );
container.addChild( btn );
btn = new Btn( { label:"3", sceneId:new SceneId( "/index/3" ), x:btn.x + 5 + btn.width, y:225 } );
container.addChild( btn );
addScene( new ChildScene( "1", { bmp:new Bitmap( new Img1( 250,250 ) ) } ) ) as ChildScene;
addScene( new ChildScene( "2", { bmp:new Bitmap( new Img2( 250,250 ) ) } ) ) as ChildScene;
addScene( new ChildScene( "3", { bmp:new Bitmap( new Img3( 250,250 ) ) } ) ) as ChildScene;
field = new TextField();
field.defaultTextFormat = new TextFormat( "_ゴシック", 10, 0x333333 );
field.x = 5;
container.addChild( field );
}
public var field:TextField;
protected override function atSceneInit():void{
//子シーンをランダムに選ぶ
var i:Number = Math.round( numScenes * Math.random() );
i = ( i == numScenes ) ? 0 : i;
var scene:SceneObject = getSceneAt( i );
addCommand(
new Prop( field, { text:sceneId.path } ),
//子シーンexecutorを動かす、シーン到着時のイベントを送信
new DoExecutor( scene.executor, new SceneEvent( SceneEvent.SCENE_INIT ) )
);
}
protected override function atSceneGoto():void{
addCommand(
new RemoveChildAt( container, 0 )
);
}
}
}
DoExecutorのサンプル
こんな形で使うと似たような処理をいくつものSceneObjectに書くようなことを減らせることもありそうですね。
ここでは、もともとSceneObjectに実装されているイベントでExecutorObjectを動かしていますが、独自のイベントでExecutorObjectを動かすことも可能です。
このあたりExecutorObjectってなに?ってところから、解りやすく書かれている記事を見つけました。
Progression 4 の DoExecutorコマンドを使い倒す!
DoExecutorコマンドは、動かしたExecutorObjectの処理でもコマンドシークエンスが有効ってところがとても便利ですね。
埋め込みフォントをSWCに書き出して利用すれば、高速なパブリッシュが出来んだが、Flashで利用する際、アウトラインフォーマットがクラシック(DF3)で、SWCに書き出したフォントと同じフォントを指定したテキストフィールドがあるとうまくSWCのフォントを利用できなかったのでメモしておく。
SWCに書き出すほうのflaファイルでは以下のように日本語フォントを埋め込んでSWCに書き出します。パブリッシュのSWF設定、SWC書き出しにチェックでSWCに書き出せる。


SWCを利用するほうのflaファイルで、SWCにパスを通し書き出したクラス名のインスタンスを宣言すると埋め込みフォントとして利用できるようになる。しかもパブリッシュは軽い。
package{
import flash.display.Sprite;
import flash.text.*;
public class EmbedDF3 extends Sprite{
public function EmbedDF3(){
//SWCに埋め込んだフォントを利用
var embedFont:Font = new HiraginoKakuGoProW3DF3();
//利用できる埋め込みフォントを確認
var arr:Array = Font.enumerateFonts();
for each( var i:Font in arr ){
trace( i.fontName ); //出力:Hiragino Kaku Gothic Pro W3
}
var str:String = "SWCの埋め込みフォントを利用" + "\n";
str += "Hiragino Kaku Gothic Pro W3";
var format:TextFormat = new TextFormat();
format.font = embedFont.fontName;
format.color= 0x999999;
var field:TextField = new TextField();
field.defaultTextFormat = format;
field.x = 25;
field.y = 25;
field.width = 200;
field.height= 100;
field.borderColor = 0x999999;
field.border = true;
field.multiline = true;
field.wordWrap = true;
field.embedFonts= true;
field.text = str;
addChild( field );
}
}
}

ここまではいいんです。とっても便利。
しかし、flaに同じフォントを指定したテキストフィールドがあったとします。
ここではステージにテキストフィールドを配置していますがどこにあっても同じ。
このテキストフィールドのアンチエイリアスはデバイスフォントとしています。

これで書き出すと、

埋め込みフォントが利用できません。
どうも同名のフォントがあるとそちらが優先される模様。
この時は、
//利用できる埋め込みフォントを確認
var arr:Array = Font.enumerateFonts();
for each( var i:Font in arr ){
trace( i.fontName ); //出力:無し(arr.length = 0)
}
も出力されないので、埋め込みでない同名フォントが優先されているのかなと。
試しにステージに配置したテキストフィールドのアンチエイリアスを「アンチエイリアス(アニメーション優先)」とした場合、テキストフィールドに入っている文字だけが表示される。

traceの部分では、Hiragino Kaku Gothic Pro W3が出力されているんだけど、Flashが自動的に埋め込む、ステージに配置したテキストフィールドのフォントになっているんだと思う。
三木さんのサイトをプチアップデートしました。
Twitterのつぶやき表示を追加しました。あと新しい三木さんの作品が5点追加されています。
46のPVが個人的に好きな感じ。応援歌にぴったりな映像にぐっときてしまいました。
前にエントリーした不具合の件、公式に載っていました。
SWFAddress.back() doesn’t work in Safari 5
Safari 5とDebugger バージョンのFlash PlayerだとNGらしい。試しに通常バージョンのFlash Playerにしたらちゃんと動いていた。作る人はDebuggerのほう使うだろうに。めんどくさい話だなぁ…。
ちなみにSWFAddress 2.1まで落とすとDebuggerでも動く。なんでって話だけど調べる気にはなれん…。一概にSafari 5のせいとも言えない話でした。

ファッションブランド、BLACKOPERATORの新作がWebで公開されました。
RAIDSYSTEM、およびWORLDS END GIRLFRIENDとコラボレーションしたT-Shirtsがリリースされています。
Webのコンテンツは今回もnssgraphica町田さんとのお仕事で、僕は引き続きFlashをやらせていただきました。
Photoshopで選択範囲を取ったときの点線の輪郭線を描きたくて試した方法。
輪郭線を描きたい対象を透明なBitmapDataにdrawする。
輪郭を得るために一旦このBitmapDataを透明と黒100%で二値化する。
var rect:Rectangle = new Rectangle( 0, 0, 200, 200 );
var master:BitmapData = new BitmapData( rect.width, rect.height, true, 0x00000000 );
var pt:Point = new Point();
master.draw( container );
master.threshold( master, rect, pt,">",0x00000000, 0xFF000000, 0xFFFFFFFF, false );
エッジ部分のアンチエイリアスも含めて輪郭としたかったんで、ここでは透明以外のピクセルはすべて黒100%にしているけど0×00000000を変更すれば、輪郭に含むアンチエイリアスの許容値を調整できる。

元の文字と二値化した文字。
二値化したBitmapDataを複製しておく。元のBitmapDataにConvolutionFilterを適用。
以下のマトリックスで適用すると境界の内側、外側に1ピクセルずつ不透明ピクセルが出来て滲んだ画像になる。ConvolutionFilterって前にエントリー書いたけど、なるほどこんなケースで使えるんだなぁと思いました。
var convolution:ConvolutionFilter = new ConvolutionFilter( 3,3,
[
1, 1, 1,
1, 1, 1,
1, 1, 1
], 9, 0, false );
var clone:BitmapData = master.clone();
master.applyFilter( master, rect, pt, convolution );
master.draw( clone );
次に滲んだBitmapDataに複製しておいたBitmapDataをdrawする。これで輪郭の外側1ピクセルが滲んだ状態になる。

輪郭の内側・外側が滲んだ文字と、輪郭の外側が滲んだ文字。
輪郭の外側の滲んだ部分が選択範囲の境界線として使いたい部分なのでこれを取り出す。
BitmapDataに対し今度は黒100%の部分を透明に置き換えた後、再度、透明と黒100%で二値化する。
master.threshold( master, rect, pt,"==",0xFF000000, 0x00000000, 0xFFFFFFFF, false );
master.threshold( master, rect, pt,">" ,0x00000000, 0xFF000000, 0xFFFFFFFF, false );
これで選択範囲の境界線が出来た。
続いてチカチカ流れる部分。これ一時悩んだのですが(輪郭を時計回りに回っているものだと思った)よく見ると水平線は左から右へ、垂直線は上から下へ流れているだけ。難しく考え過ぎてた。斜めの線動かせばいんじゃない?ってYさんの一言で気づいた。
斜め線のパターンを作ってBitmapDataのサイズ同じサイズでShapeを塗りつぶす。
var b:uint = 0xFF000000;
var pattern:BitmapData = new BitmapData( 6,6, true, 0x00000000 );
pattern.setVector( new Rectangle( 0,0,6,6 ),
Vector.<uint>([
0,0,0,b,b,b,
0,0,b,b,b,0,
0,b,b,b,0,0,
b,b,b,0,0,0,
b,b,0,0,0,b,
b,0,0,0,b,b
])
);
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
g.beginBitmapFill( pattern );
g.drawRect( 0,0, rect.width, rect.height );

選択範囲の境界線と斜め線のパターン。
あとは斜め線のパターンを1ピクセルずつ動かしながら選択範囲の境界線に合成すれば、Photoshop風の点線境界線の完成です。
var matrix:Matrix = new Matrix();
addEventListener( Event.ENTER_FRAME, function(e:Event):void{
matrix.tx = ( matrix.tx + 1 ) % 6;
g.clear();
g.beginBitmapFill( pattern, matrix );
g.drawRect( 0,0, rect.width, rect.height );
outline.fillRect( rect, 0 );
outline.draw( master );
outline.draw( shape, null, null, BlendMode.ALPHA );
});

5.0.1になって、この間の画面が溶ける現象がなおったと思ったら、こんどはSWFAddressが効かなくなったような気が…。いい加減にしてほしい。
直リンクはOKですけど、ブラウザの戻る進むの時反応しなくないですか?俺だけ?
http://www.asual.com/swfaddress/samples/flash/
バージョン 5.0.1 (6533.17.8)
phpやperlのプログラマーの方で、一緒に仕事してもいいよ。
というフリーランス・個人の外部パートナーを募集しています。
サーバーサイドのプログラムが必要な案件では、こちらで対応しきれないことがあります。案件の数は多くないけど、そんな話があった時に見積もりレベルから気軽に相談できる方がいたらいいなと。もちろん逆にFlashやデザインが必要な時には相談に乗らせていただきますので。
それほど複雑な案件は過去ありませんので、企業キャンペーンサイトのクライアント案件実績があればスキルよりも誠実・堅実・責任感のある方で都内で打ち合わせ可能な方。
よかったらご連絡ください。まずは一度お会いしてみませんか。
気が合いそうでしたら一緒に仕事をしましょう。
info@nipx.jp
【過去にあった具体的な案件例】
Twitter APIを操作したい。
サーバーサイドで画像生成したい。
ゲームのハイスコアを記録したい。
トラックバックを集計したい。
MTやWPのカスタマイズなどで簡易CMSにしたい。