
Flash サイトのページ内で移動すると、ブラウザの戻るボタンが聞かないとか、直接あのページを紹介したいのにあのページの URL (パーマリンク)がないとかいう話はもはや過去。今は SWFAddress でフルフラッシュサイトの全ページにパーマリンクを貼って、戻るボタンやなんやらでサクサクみれるフル Flash は作れるようになっているのです。
そんな用途に使う ActionScript/JavaScript ライブラリ「SWFAddress」について。
SWFAddress とは?
Flash と JavaScript が通信を行い、JavaScript がブラウザと「アドレス」や「タイトル」、「履歴」などのコミュニケーションをおこなうことで、 Flash がブラウザとコミュニケーションしているかのように見せる方法を提供する、 ActionScript/JavaScript ライブラリの名前。
ブルガリアのソフィアにある Asual DZZD (http://www.asual.com/)という会社がオープンソースかつ MITLicence で公開しています(要は自由に使っていいけど自己責任で使ってね、あと SWFAddress のソース内にある僕の著作権表示は消さないでねライセンス)。
Flash 内でパーマリンクを設定したいページに遷移してきたときに、ActionScript で SWFAddress に値を設定すると、ブラウザのアドレスバーにパーマリンクが設定されている、という仕組み。SWFAddress 公式のサンプルがこちら。
ただ僕は何でもかんでも SWFAddress を使うべきではないと思っています。それは IE だとページ遷移の「カチッ」という音が Flash サイトであるにも関わらず鳴るのが気持ち悪いというのもありますが、演出を要するようなサイトで、演出をすっとばして見れるようにする URL を用意すると言うのは、「映画の後半などの、ここだけ見ればいいというポイントへ直接飛べる」的な「便利にするが故に伝えるべきものが伝わらなくなる」ということになるかもしれないと思うからです。
ショッピングサイトや、サービス系のサイトで全てまたは大部分のページに用意するのは有りだと思います。しかし、物をエモーショナルに伝えるべきスペシャルサイトなどでは、特定のチェックポイントだけにしたり、いっその事全く対応しないでおいたり、広告戦略を含めたサイトの役割・立ち位置に応じて臨機応変に使い分ける事を熟考すべきだと思います。
(話の前半の「戻るボタンが効かない」(つまり戻るボタンに頼りたくなる)というのは、デザインやモーションに問題がある気がします。フローティングを開いたときに「閉じるボタン」が分かりにくいとか。(戻るボタン対応すればマウスジェスチャとか使えるというのはあるけど)クリエイターならそこのクオリティを上げて対応したいところ?)
AS3 で SWFAddress 2.4 を使う
ここからは Flasher (実装者)向けの話。SWFAddress を使う場合は Progression や Gaia とかフレームワークを使ったりする方が色々良いと言う話を聞きますが男なら自力で実装。(SWFAddress も自分で実装しろよというツッコミは NO THANK YOU)
今日現在は 2.4 が最新なので最新を利用。このページから SWFAddress をダウンロードして、解凍してできたフォルダの中の sample ディレクトリ内に各種サンプルがありますのでこれらを利用します。
今回は FlashPlayer9 以上、AS3 で作るので「cs3」フォルダから SWF を表示する HTML と index.html と swfaddress フォルダ、 swfobject フォルダ、SWFAddress.as、SWFAddressEvent.as をどこか好きな場所にコピー。index.html に埋め込む SWF のファイル名を指定。 SWFAddress は SWFObject という JS で SWF を埋めこまなければなりません。
AS では SWFAddress.setValue() でパーマリンクを設定したり、 SWFAddress.getPathNames() などを使って遷移を分岐させていったりします。
サンプル。
SWFAddressTest.as
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import SWFAddress;
import SWFAddressEvent;
/**
* for SWFAddress 2.4
*/
public class SWFAddressTest extends MovieClip
{
private var _tf:TextField;
private var _defaultTitle:String;
public function SWFAddressTest()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
_tf = new TextField();
addChild(_tf);
_tf.multiline = true;
_tf.autoSize = TextFieldAutoSize.LEFT;
_tf.width = 200;
_tf.height = stage.stageHeight;
_tf.text = "";
SWFAddress.addEventListener(SWFAddressEvent.INIT, initHandler);
}
private function setButtons():void
{
var names:Array = [
"zero",
"one",
"two/2",
"three/3/3",
"four/4/4/4/",
"popup()",
"href()",
"forward()",
"back()",
"up()",
"go(-2)",
"resetStatus()"
];
for (var i:int = 0; i < names.length; i++)
{
var btn:Sprite = new Sprite();
btn.buttonMode = true;
btn.addEventListener(MouseEvent.CLICK, clickHandler);
btn.graphics.beginFill(0x101226);
btn.graphics.drawRect(0, 0, 100, 30);
btn.name = names[i];
addChild(btn);
btn.x = stage.stageWidth - 120;
btn.y = i * 40 + 20;
var tfm:TextFormat = new TextFormat();
tfm.color = 0xEFEDD9;
var tf:TextField = new TextField();
tf.defaultTextFormat = tfm;
tf.selectable = false;
tf.text = names[i];
btn.addChild(tf);
}
}
private function clickHandler(event:MouseEvent):void
{
var target:Sprite = Sprite(event.currentTarget);
// クエリ文字列が setValue によって消えてしまうので、あればくっつける
var queryString:String = ( SWFAddress.getQueryString() ) ? "?" + SWFAddress.getQueryString() : "";
switch(target.name)
{
case "zero":
case "one":
case "two/2":
case "three/3/3":
case "four/4/4/4/":
SWFAddress.setValue(target.name + queryString);
break;
case "popup()":
tracer("------------------");
tracer("ポップアップウィンドウ起動(option値= width=320, height=230, scrollbar=NO)");
tracer("正直動く気がしない");
SWFAddress.popup("http://feb19.jp/blog/archives/000181.php", "popup", "width=320, height=230, scrollbars=NO");
break;
case "href()":
tracer("------------------");
tracer("_target でリンク");
SWFAddress.href("http://feb19.jp/blog/archives/000181.php", "_target");
break;
case "forward()":
tracer("------------------");
tracer("SWFAddress の履歴を一つ進む");
SWFAddress.forward();
break;
case "back()":
tracer("------------------");
tracer("SWFAddress の履歴を一つ戻る");
SWFAddress.back();
break;
case "up()":
tracer("------------------");
tracer("SWFAddress の階層を一つ上がる");
SWFAddress.up();
break;
case "go(-2)":
tracer("------------------");
tracer("SWFAddress の履歴を2つ戻る(引数に 1 を指定すると 1 つ進み、-3 を指定すると 3 つ戻る)");
tracer("履歴が 1 しか残っていなくて、2 つ戻るを指定すると戻らない");
SWFAddress.go(-2);
break;
case "resetStatus()":
tracer("------------------");
tracer("top.document.status = \"\"; を実行(ステータスバーをクリア)");
SWFAddress.resetStatus();
break;
}
}
private function tracer(value:*):void
{
if (value is String)
_tf.text = value + "\n" + _tf.text;
else
_tf.text = value.toString() + "\n" + _tf.text;
}
private function initHandler(event:SWFAddressEvent):void
{
SWFAddress.removeEventListener(SWFAddressEvent.INIT, initHandler);
tracer("------------------");
tracer("parameterNames: " + event.parameterNames);
tracer("parameters: " + event.parameters);
tracer("path: " + event.path);
tracer("pathNames: " + event.pathNames);
tracer("target: " + event.target);
tracer("type: " + event.type);
tracer("value: " + event.value);
tracer("------------------");
// SWF を呼び出す HTML の URL を取得
tracer("getBaseURL: " + SWFAddress.getBaseURL());
// 履歴をセットするか
tracer("getHistory: " + SWFAddress.getHistory());
// クエリ文字列にあるキー名を引数で指定するとその値がとれる。
// クエリ文字列とは URL の後ろにつくことがある「?」以降の文字列。
// ?abc=1&unko=suteki というような感じ。
// この場合は unko キーの値「suteki」を取得できる。
tracer("getParameter: " + SWFAddress.getParameter("unko"));
// クエリ文字列のキー名一覧を配列で取得する。
tracer("getParameterNames: " + SWFAddress.getParameterNames());
// SWFAddress の path 文字列(#以降の文字列)を取得する。クエリ文字列は無視する。
tracer("getPath: " + SWFAddress.getPath());
// SWFAddress の path を配列で取得(「/」を区切りにして配列に格納してくれる)
// 個人的にはアドレスの判別はこれを使うのが便利かつシンプルな気がします。
// クエリ文字列は無視する。
tracer("getPathNames: " + SWFAddress.getPathNames());
// クエリ文字列の取得。(「?」以降の文字列)
tracer("getQueryString: " + SWFAddress.getQueryString());
// ステータスバーの値を取得。
tracer("getStatus: " + SWFAddress.getStatus());
// SWFAddress 文字列(SWFAddressEvent.value)の先頭に「/」がつくか
tracer("getStrict: " + SWFAddress.getStrict());
// ページのタイトルを取得する。
tracer("getTitle: " + SWFAddress.getTitle());
// ページ遷移中にトラッキングする際呼び出す関数 初期値は urchinTracker
tracer("getTracker: " + SWFAddress.getTracker());
// #以降の文字列。クエリ文字列も含む。
tracer("getValue: " + SWFAddress.getValue());
_defaultTitle = SWFAddress.getTitle();
setButtons();
SWFAddress.addEventListener(SWFAddressEvent.CHANGE, changeHandler);
SWFAddress.addEventListener(SWFAddressEvent.EXTERNAL_CHANGE, externalChangeHandler);
SWFAddress.addEventListener(SWFAddressEvent.INTERNAL_CHANGE, internalChangeHandler);
}
private function changeHandler(event:SWFAddressEvent):void
{
tracer("------------------");
// パラメータのキー名一覧を配列で取得
tracer("parameterNames: " + event.parameterNames);
// パラメータ全てを連想配列(Object 型)で取得
tracer("parameters: " + event.parameters);
// SWFAddress の path 文字列(#以降の文字列)を取得する。クエリ文字列は無視。
tracer("path: " + event.path);
// SWFAddress の path を配列で取得(「/」を区切りにして配列に格納してくれる)。
tracer("pathNames: " + event.pathNames);
tracer("target: " + event.target);
tracer("type: " + event.type);
tracer("value: " + event.value);
// .pathNames[0] の値がどうかでウィンドウタイトルを変更する
switch(event.pathNames[0])
{
case "zero": SWFAddress.setTitle( _defaultTitle + " - Content 0" ); break;
case "one": SWFAddress.setTitle( _defaultTitle + " - Content 1" ); break;
case "two": SWFAddress.setTitle( _defaultTitle + " - Content 2" ); break;
case "three": SWFAddress.setTitle( _defaultTitle + " - Content 3" ); break;
case "four": SWFAddress.setTitle( _defaultTitle + " - Content 4" ); break;
default: SWFAddress.setTitle( _defaultTitle + " - Content Unknown" );
}
}
private function externalChangeHandler(event:SWFAddressEvent):void
{
tracer("------------------");
// パラメータのキー名一覧を配列で取得
tracer("parameterNames: " + event.parameterNames);
// パラメータ全てを連想配列(Object 型)で取得
tracer("parameters: " + event.parameters);
// SWFAddress の path 文字列(#以降の文字列)を取得する。クエリ文字列は無視。
tracer("path: " + event.path);
// SWFAddress の path を配列で取得(「/」を区切りにして配列に格納してくれる)。
tracer("pathNames: " + event.pathNames);
tracer("target: " + event.target);
tracer("type: " + event.type);
tracer("value: " + event.value);
}
private function internalChangeHandler(event:SWFAddressEvent):void
{
tracer("------------------");
// パラメータのキー名一覧を配列で取得
tracer("parameterNames: " + event.parameterNames);
// パラメータ全てを連想配列(Object 型)で取得
tracer("parameters: " + event.parameters);
// SWFAddress の path 文字列(#以降の文字列)を取得する。クエリ文字列は無視。
tracer("path: " + event.path);
// SWFAddress の path を配列で取得(「/」を区切りにして配列に格納してくれる)。
tracer("pathNames: " + event.pathNames);
tracer("target: " + event.target);
tracer("type: " + event.type);
tracer("value: " + event.value);
}
}
}
SWFAddressEvent.INIT 後に getQueryString などをしないとエラーになったりするので(未初期化のため)、SWFAddress の操作は INIT 後から。
SWFAddressEvent.CHANGE イベントで、SWFAddress.setValue() した瞬間(INTERNAL_CHANGE)や、ブラウザで戻るボタンが押されたり、アドレスバーが変更された瞬間(EXTERNAL_CHANGE)を監視し、そのイベントハンドラで遷移を実装していきます。最新版の 2.4 から INTERNAL_CHANGE や EXTERNAL_CHANGE を取る事ができるようになりました。
サンプルでは switch case で分岐して、ブラウザのタイトルを変更してみています。
ちなみにサンプルを触って気づいたと思いますが、 SWFAddress にはアドレスを操作するだけではなく、ブラウザの各種機能を使う機能が用意されていますが、現時点では SWFAddres.popup() は挙動がおかしいですし(Flash からポップアップウィンドウだすならこちら)、ステータスバー関連もやや怪しいです。
現状アドレスの操作以外では、クエリ文字列(URL に付くことがある「?」以降の文字列)操作や、タイトルの操作ぐらいにとどめておいた方が良さそう。トラッカーもちょっと古い。
以上、現時点で SWFAddress を使う場合のメモでした。Flasher 以外も最近は覚えておくべきかと思って前半はちょっと普段より比較的平易な文章にしてみました。あと、これやっぱり結構実装めんどくさくてちょっとクセがあるので、途中でちょっと出ましたが Progression や Gaia みたいなフレームワークを利用するのもやっぱり有りだと思います。(なんだよそれ男じゃねーじゃんというツッコミも NO THANK YOU)
AS1 や AS2 で実装したい場合は公式のサンプルを参考に。Flash 8 以上なら使えます。

