« October 2006 | Main | December 2006 »

November 30, 2006

flash_proxy PART1(JP)

Proxyというのは、そもそも実際の処理を代行するもので自分の中で処理できない内容はクラスの後ろに設定してある実体にその処理を任せる。flash_proxyはネームスペースという考え方が入っているので若干複雑かもしれないが、この機能を使うと実際には作っていない(未定義の)メソッドやプロパティを呼び出すことが出来る。例えば、実行時に何か変数を列挙したドキュメントを読み込んだりすることはよくあるが、この変数が既に定義されていたかのように取り出すことが出来たり、他のクラスの関数を自分の中に定義されているかのように振る舞うことが出来る。

callProperty
この例では_targetというオブジェクトにmovieclipという実体が設定されている前提になっている。movieclipなら実際はどんなメソッドがあるのか分かるし、直接呼んでも問題はないが、実行時に新たに誰かが設定したメソッドがあるとしたら、それを実体に対して呼び出すことは出来ない。実体でメソッドが未定義だったら他のオブジェクトのメソッドを実行するということも出来る。proxyという存在でワンクッション置いてあるので後ろの実際には表面から見えない実装が柔軟になるのである。

dynamic class ExProxy extends Proxy{
  var _target:Object;
  flash_proxy override function callProperty(method:*, ...param):*{
     _target[method].apply(_target, param);//_target.method(p1,p2..)のように実行
  }
}
exProxy.gotoAndStop(1);//_funcにmovieclipが設定されている仮定

getProperty
前述の例どおり、読み込んだテキストから変数をセットすることは出来ない。読み込んだデータが入ったオブジェクトの中の変数を直接呼んでいるかのようにアクセスできると便利である。

dynamic class ExProxy extends Proxy{
  var _data:Object;
  flash_proxy override function getProperty(name:*):*{
     return _data[name];
  }
}
trace(exProxy.name);//EXProxyクラスにないプロパティを要求しても正しい結果

AS2 to AS3 migration(JP)

たまに使うコマンドの変更点の列挙。

asfunction
asfunctionはAS3には無く、eventDispatcherで処理する。eventを発生させたいtext箇所にevent:文字列を埋め込む。

field.htmlText="<a href='event:login'>click to login</a>";
addEventListener(TextEvent.LINK, onLinkEvent);
function onLinkEvent(evt:TextEvent):void{
  trace(evt.text);//login
}

updateAfterEvent()
強制再描画関数はもうグローバル関数ではない。timer、mouse、keyboardのイベントクラスから呼び出せる。

instanceof
全オブジェクトをループで列挙しながら特定の型を確認するケースはまれにあるが、今後はisを使う。

for(var i in this){
  if(this[i] is Sprite){
    trace("SPRITE");
  }
}

November 28, 2006

MouseWheel on Mac(JP)

デルタ値
OSXではflashでマウスホイールの回転量(デルタ値)を取れないので、テキストスクロールなどに応用が残念ながら出来なかった。flash9を使ってas3でもOSXでは、このデルタ値を取得できない。ではjavascript経由でdeltaを取得し、flash内で使おうというものがこのサイト。まず、swfmacmousewheelからjs一式をdownloadする。サンプルのindex.htmlを実行するとエラーが起こる。実はswfaddressを必要とするのでそちらを落とし、ファイルをjsフォルダに入れると正しく動作する。ホイールの回転に合わせてflash内のテキストフィールドの数値が変わるのが確認できる。

base.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
private function onMouseWheel(evt:MouseEvent):void{
	trace(evt.delta);
}

ブラウザのヒストリ
ちなみにswfAdressも優れもので、他種類のブラウザ、いわゆるクロスブラウザ対応でブラウザのヒストリ機能が使える。サンプルではflash内のボタンの操作に合わせてページがflash内で変わり、さらにブラウザにヒストリをセットする。ブラウザの戻るボタン操作でflash内のページを切り替えれる。

javascriptでファイルをアップロード
さらにもう一つ、swfuploadは、javascriptからswfのアップロード選択画面を呼び出してアップロードができる。flashを使えない場面(?)でxhtmlとcssでprogressバーを表示したりもできるが、もともとjavascriptだけを使ったformタグでのアップロードではアップロードの完了でしかファイルをアップロードが通知されないのでユーザはファイルが大きいと長い時間待たされてしまう。PHPなどのサーバ側のプログラムに若干手を加えること(アップロード中の/tmpフォルダのサイズを途中途中で取得するような)が出来れば、アップロード中のプログレスバーを表示できる。

November 19, 2006

Event伝搬を理解する(JP)

ネスケを恨んだ制作現場
AS3の理解は大切になってきている。過去web制作を経験したものなら、IEやネスケ、特にネスケには様々なバージョンがあったことを知っている。今でこそfirefoxが主流となりつつありIEが悪者扱いされているが、当時はネスケの動作を正常にするためには大幅な作業時間を割かざる得なかった。クロスプラットフォームのウェブサイトをjavascriptでインタラクティブな動作も加えてユーザに見せたいというクライアントの欲求は当時からあった。当時自分は 永谷園のサイトでフレームを使わないでフレームを使っているようにメニューが切り替わるものを制作した。フレームを使えば押されたボタンのメニューページを何十ページも制作しなければならない。しかしフレーム無しでDHTMLがあれば、メニューが1ページで制作が済む。当時からオブジェクト指向のjavascriptでメニューの制作をしていたので、コードがコンパクトで見やすかったものの、一番苦労したのはブラウザの差を消す部分。制作現場で主流だったmacのIE4.5や古いネスケでいかに同じように見えるかということには結果的に一番時間をかけてしまった。ネスケの話はActionscriptでも同じことが言えないだろうか。一部のネスケ愛好家やOS9の根強いユーザがやはりウェブ制作現場の新しい技術へ移行しにくくしていたことは否定できない。古いバージョンのアクションスクリプトをいつまでも使い続けることは少数派利用のブラウザや古いOSのユーザを恨み妬んだ制作者にはあってはならないことと感じる。

イベントとは何か
AS3ではイベントの伝搬の仕組みが変更されたので今までのアクションスクリプトのバージョンのマウスの動作がそのまま再現されるとは限らない。例えばonReleaseOutsideというものは存在しない。そもそもイベントとは何かを理解していく。
簡単に理解できるようにマウスを例に挙げると、マウスのボタンが押されたとき、離されたとき、ホイールを転がしたときにflashはある関数を自動的に呼び出している。この関数にある処理をすることでマウスで何かが起きたときに制作者がしたい動作をアクションスクリプトで記述することができる。この時、制作者がしたい動作がイベントハンドラメソッドであり、そのイベントハンドラを呼び出す登録がaddEventListenerと理解できる。

function onMouseClick(e:MouseEvent):void{
//マウスを押したらしてほしい動作
}
button.addEventListener(MouseEvent.CLICK, onMouseClick);//buttonで起きたクリックでonMouseClickという動作をする登録処理

イベントの伝搬
buttonでクリックが起こった場合、buttonのみがクリックが起きたことを知るわけではない。確かにbuttonをクリックすればクリックが起きたのはbuttonだが、最上位の親からbuttonに至るまでの通るすべてのobjectもそのクリックを知っている。ただし、それを知るのは親もリスナー登録した場合に限られる。 この例ではstageがリスナー登録をしているので、buttonがクリックされるとstageにもクリックが知らされる。その場合クリックが知らされるのは、buttonにeventが知らされた後である。イベントの伝搬にはcapture、event、bubblingの3つのフェーズがある。AS2までで意識していたのはeventのフェーズのみなので、少し分かりづらいかもしれない。

button.addEventListener(MouseEvent.CLICK, buttonClick1);//event処理
button.addEventListener(MouseEvent.CLICK, buttonClick2);//event処理
stage.addEventListener(MouseEvent.CLICK, stageClick1);//bubbling処理
function buttonClick1(e){
trace("click1");
}
function buttonClick2(e){
trace("click2");
}
function stageClick1(e){
trace("stage1");
}
//----
click1
click2
stage1

親に先に知らせたい
buttonに知らされる前にstageに知らせたい場合もあるかもしれない。その場合はuseCaptureという機能を使う。useCaptureを記述しない又はfalseの場合は、eventとbubblingのフェーズのみが通知される。

button.addEventListener(MouseEvent.CLICK, buttonClick1);//event処理
button.addEventListener(MouseEvent.CLICK, buttonClick2);//event処理
stage.addEventListener(MouseEvent.CLICK, stageClick1,true);//capture処理
stage.addEventListener(MouseEvent.CLICK, stageClick2);//bubbling処理
function buttonClick1(e){
trace("click1");
}
function buttonClick2(e){
trace("click2");
}
function stageClick1(e){
trace("stage1");
}
function stageClick2(e){
trace("stage2");
}
//----
stage1
click1
click2
stage2

stageをクリックしたらどうなるか
stageをクリックすると、子へ伝える必要が無いので、stage止まりになる。前節のとおり、false又は省いた場合はeventとbubblingを処理する。ここではstageでクリックというeventが発生しているのでstageClick2イベントハンドラで処理される。capturingは子でeventが起きた場合に起こるのでここでは呼び出されない。

button.addEventListener(MouseEvent.CLICK, buttonClick1);
button.addEventListener(MouseEvent.CLICK, buttonClick2);
stage.addEventListener(MouseEvent.CLICK, stageClick1,true);
stage.addEventListener(MouseEvent.CLICK, stageClick2);//event処理
function buttonClick1(e){
trace("click1");
}
function buttonClick2(e){
trace("click2");
}
function stageClick1(e){
trace("stage1");
}
function stageClick2(e){
trace("stage2");
}
//----
stage2

イベント伝搬を途中でやめる
子でクリックされた後に親にそのことを伝えたくない場合もある。stopPropagation()を使うとeventが発生している場所で処理が終わった後の伝搬が停止する。この例ではbuttonでeventが起きているので、buttonClick1とbuttonClick2が処理されその後のstageClick2が行われない。

button.addEventListener(MouseEvent.CLICK, buttonClick1);//event処理(+stopPropagation)
button.addEventListener(MouseEvent.CLICK, buttonClick2);//event処理
stage.addEventListener(MouseEvent.CLICK, stageClick1,true);//capture処理
stage.addEventListener(MouseEvent.CLICK, stageClick2);
function buttonClick1(e){
e.stopPropagation();
trace("click1");
}
function buttonClick2(e){
trace("click2");
}
function stageClick1(e){
trace("stage1");
}
function stageClick2(e){
trace("stage2");
}
//----
stage1
click1
click2
stopImmidiatePropagation()を使うと即座に伝搬が停止するので、この例ではbuttonClick1以降が実行されない。
button.addEventListener(MouseEvent.CLICK, buttonClick1);//event処理(+stopImmidiatePropagation)
button.addEventListener(MouseEvent.CLICK, buttonClick2);
stage.addEventListener(MouseEvent.CLICK, stageClick1,true);//capture処理
stage.addEventListener(MouseEvent.CLICK, stageClick2);
function buttonClick1(e){
e.stopImmidiatePropagation();
trace("click1");
}
function buttonClick2(e){
trace("click2");
}
function stageClick1(e){
trace("stage1");
}
function stageClick2(e){
trace("stage2");
}
//----
stage1
click1

useCaptureのまとめ
stageに対してのリスナー登録でuseCaptureをtrue又はfalse(デフォルト)にしたときの動作をまとめると次のようになる。buttonでCLICKイベントが起きたとき、stageのuseCaptureがtrueであれば、buttonでeventフェーズの処理が行われる前にstageのcaptureフェーズで呼ばれる。その後、buttonのeventが呼ばれ、stageのbubblingが呼ばれる。基本的にはbuttonのeventだけを処理すると考えるのが一般的だろう。useCaptureがfalseであれば、buttonのeventがまず最初に呼ばれ、その後にstageのbubblingと続く。一方stageをクリックした場合もCLICKイベントが呼ばれるが、stageのeventであるので、captureとbubblingは行われない。eventを処理するのはfalse(デフォルト)の場合のみとなる。

November 17, 2006

[mdm]FLV再生プレーヤー後編(JP)

ファイルが再生できる形まで前回完成していたので、あとはzincで書き出すだけ。再生時間と再生過程を表すバーを追加したい。このバーはドラッグするとその位置から再生する機能も追加したい。

mdmスクリプトの準備
まず前回後回しにしていたmdmスクリプトの基本部分を考えていく。as3でmdmスクリプトを使う場合、flex builderに設定をする必要があり、まずzincをインストールしたディレクトリにswcファイルがあることを確認する。無い場合は、バージョンが古いので、updateをzincのページからダウンロードしてインストールする。swcファイルはmdmスクリプトのライブラリが入っているので、これをflex builderのライブラリパスにadd swc...ボタンから追加を行う。追加後、mdmスクリプトを使うクラスにimport mdm.*;を行う。

初期化
今回はflexではなくasのプロジェクトなので、まずコンストラクタに次のようにコールバック関数を加えた一文を挿入する。onInitメソッドを後で加える必要があるが、このプロジェクトではコンストラクタですべて行っているのでonInitは空にしてある。

mdm.Application.init(this, onInit);

時間をフォーマットする
現在の再生時間とムービーの全体の時間を表示する場合、再生時間はstream.timeで取得できるが、全体の時間はstreamにはなく、movieファイルであるflvがメタデータとして持っているので、as2でもあったonMetaDataを使う。as3では、flv自体はstream.clientプロパティとなるので、次のようになる。

stream=new NetStream(conn);
var client:Object=new Object();
client.onMetaData=onMetaData;
stream.client=client;
private function onMetaData(data:Object):void{
	_duration=data.duration;
}
_durationとstream.timeは秒表示なので時間を00:00:00というわかりやすい形にしたい。これには前回markXのサイトでも同様のものを作成したので同じ考え方で次のような関数を用意した。
private function _formatTime(time:Number):String{

	var hh:* = Math.floor(time / 3600);
	var mm:* = Math.floor(time / 60);
	var ss:* = Math.floor(time % 60);
	
	if (ss<10) {
		ss = "0"+ss;
	}
	if(mm<10){
		mm = "0"+mm;
	}
	if(hh<10){
		hh = "0"+hh;
	}
	return hh + ":" + mm + ":" + ss;
}
ss、hhやmmが文字列でありながら数値でもあるのでエラーが起きるので*型にしている。

プログレスバー
バーはドラッグできる。ドラッグしたい時はstartDrag()を使う。

private function onThumbPress(e:MouseEvent):void{
//area:ドラッグできる範囲
	var area:Rectangle=new Rectangle(100,253,210-5,5-5);
	thumb.startDrag(false, area);
			
			
	stream.pause();
			
	_isPressing=true;
}

private function onThumbRelease(e:MouseEvent):void{
	thumb.stopDrag();
			
	seek();
	stream.resume();
			
	_isPressing=false;
}
ドラッグ中は映像もその場所を示して(シークして)欲しいので、一定時間毎に処理をする仕組みが欲しい。その場合タイマーを使う。コンストラクタに次の記述が要る。
var timer:Timer=new Timer(100);
timer.addEventListener(TimerEvent.TIMER, onTick);
timer.start();
一定毎にする仕事をonTickメソッドに記述する。バーの移動幅に合わせて尺と比較し現在のつまみの位置を決定する。シーク動作はマウスを押しているときのみ、要するにドラッグ中のみし、ドラッグしていない時は逆にバーの位置は再生状況にあわせて時間から位置を設定する。この位置設定はドラッグ中にはいらないので次のようになる。
private function onTick(e:TimerEvent):void{
	var size:Number=210-5;//移動最大幅
	var position:int=stream.time/_duration*size;
			
	if(_isPressing){
		seek();//シーク
	}else{
		thumb.x=100+position;//押してない時は位置を変える
	}
	label.text=_formatTime(stream.time) + "/" + _formatTime(_duration);
}

時間の表示もここで更新する。若干ドラッグ回りにはバグがあるのでさらに修正などがいるが、アプリとして機能するバージョン(MACOSX用)はここにある。このソースファイルも落とすことが出来る。

[mdm]FLV再生プレーヤー(JP)

今回はOSX上でFLVを単体で再生できるプレーヤーアプリケーションをMDMのZincとas3を使って作成する。as3はさほど難しい部分は扱わないので、今回の例は基本的な学習にちょうどいいかもしれない。

FLVを再生する
この例で大切なのは、FLVを再生する機能、ローカルファイルを検索する機能の2点のみ。まずFLVの再生はこのブログで過去にas2版を載せている。特に難しいことはなさそうなので、as3でこれを書き換えてみるとこのようになる。

conn=new NetConnection();
conn.connect(null);
stream=new NetStream(conn);
			
var video:Video=new Video();
video.attachNetStream(stream);
addChild(video);
細かい設定は覚えた後にいくらでも出来るので、ほとんどは基本設定のままで記述する。

画面のグラフィックを配置
addChild()という部分に違和感がある以外、ほとんどas2と違いは見られない。addChild()は以前述べたように画面に表示させるメソッドで、ここではvideoを画面に貼り付けている。as2ではビデオなどオーサリングで配置するしかないものもあり、ムービークリップはattachMovieなどで画面へ表示させるなどバラバラだったが、as3ではすべてaddChildというメソッドで統一されている。

ローカルファイルをブラウズする
映像の再生と停止も何度も記事で扱っているので省略。次に考えたいのはローカルファイルを読み込む部分である。通常ウエブではローカルファイルは扱わない。ここではデスクトップアプリケーションとしてas3でプレーヤーを作っているので、読み込むダイアログボックスを表示しなければならない。ここでは、mdmスクリプトの機能を利用する。mdmの基本設定などの使い方は後で述べるとして、まずダイアログボックスの使い方を考える。

private function onClickBrowse(e:MouseEvent):void{	
	var filename:String=mdm.Dialogs.BrowseFile.show();
}
mdm.Dialogs.BrowseFileオブジェクトにあるshow()メソッドを使うだけで表示できる。

macOSX特有のパスを扱う
表示後選択されるとfilenameにファイルのパスが入るが、キャンセルが押されるとfalseが戻る。実際はこの部分でなんらかの処理がいるかもしれない。macosではパスが:区切りなので/に置き換えるなどの処理が発生する。fileプロトコルに変換するとどうなるか教えてくれるサイトもある(例:Machintosh HD:Users:Dai:Desktop:sample.flvをfile:///Users/Dai/Desktop/sample.flvへ変換)

filename=filename.split(":").join("/");
var start:int=filename.indexOf("Users");
_filepath="file:///"+filename.substr(start, filename.length-start);
label.text=_filepath;
次回へ続く。