Java アプレットが起動するまでの「待たされている感」を小さくする方法

先日書いたエントリ の続き。
Java アプレット が完全に起動するまでの「待たされている感」を小さくする方法について、Java Plug-in のロード時間の短縮やパフォーマンス向上は僕らには難しいので、それはそちらの方々にお任せして、僕らにもできるちょっとしたハック的な方法を考えてみた。


細かい話は省略。
ここでは dtfx.js (http://dl.javafx.com/dtfx.js) という JS を利用して、アプレットをページにロードする(<applet> タグを書き出す)場合に限定するけど、自分で <applet> タグをゴリッと書く場合でも基本的な考え方は一緒。


NetBeansJavaFX プロジェクトをビルドすると dist フォルダが作成されてその下に生成される HTML ファイルから dtfx.js がロードされる。
HTML は下のような風に生成される。


ShootingFX.html

<html>
<head>
<title>ShootingFX</title>
</head>
<body>
<h1>ShootingFX</h1>
<script src="http://dl.javafx.com/dtfx.js"></script>
<script>
    javafx(
        {
              archive: "ShootingFX.jar",
              draggable: true,
              width: 720,
              height: 720,
              code: "jp.fooami.shootingfx.Main",
              name: "ShootingFX"
        }
    );
</script>
</body>
</html>


この HTML の javafx() という関数は dtfx.js で定義された関数で、この関数は更に内部で javafxString() という関数を呼び出している。
javafxString() 関数は <applet> タグとコーヒーカップのアニメーションを表示する為の <img> タグを文字列として返し、javafx() 関数がそのタグ文字列を document.write() で書き出している。


Java Plug-in と、アプレットのロードが完了するまで表示されるデフォルトのアニメーションがこれ。
アプレットが縦横 100×100 ピクセル以上の場合)


そして、アプレットは起動が完了して初期化が済むと JSObject 経由で、dtfx.js で定義された fxAppletStarted() 関数を呼び出して、アニメーションを非表示にする仕組み。


JavaFX で作成したアプリの場合は、JavaFX アプリがそれをする代わりに、com.sun.javafx.runtime.adapter.Applet というクラスのオブジェクトが fxAppletStarted() を呼び出しているんだと思う。(完全に推測)
com.sun.javafx.runtime.adapter.Applet は <applet> タグの子要素の <param> タグとして、一緒に出力されている。


話が長くなってしまっているけど (^^; ようはデフォルトのアニメーション GIF を、自前の画像に置き換えてしまえば「待たされている感」を感じさせないようにできるんじゃないかという話。
今回の例は自前の画像で置き換えるけど、別に画像でなくても何でも良い。


アプレットの準備が完了するまで、下のイメージで、アニメーションを置き換える。


その仕組みがこれ。
dtfxEx.js

var dtfxEx = {};
dtfxEx.debug = false;
dtfxEx.overlayImageURL;
dtfxEx.overlayImageWidth;
dtfxEx.overlayImageHeight;
dtfxEx.overlayImage = function(url, w, h) {
	var img = document.createElement("img");
	img.src = url;
	img.width = w;
	img.height = h;
	dtfxEx.overlayImageURL = url;
	dtfxEx.overlayImageWidth = w;
	dtfxEx.overlayImageHeight = h;
}
dtfxEx.replaceOverlayImage = function() {
	if (!dtfxEx.overlayImageURL) return;
	var img = document.getElementById("deployJavaApplet1Overlay")
		.getElementsByTagName("img")[0];
	img.src = dtfxEx.overlayImageURL;
	img.width = dtfxEx.overlayImageWidth;
	img.height = dtfxEx.overlayImageHeight;
}
dtfxEx.reserve = function() {
	var evtType = "load";
	var f = function(){
		dtfxEx.replaceOverlayImage();
	}
	if (window.addEventListener) {
		window.addEventListener(evtType, f, false);
	} else if (window.attachEvent) {
		window.attachEvent("on" + evtType, f);
	} else {
		// for IE/Mac, NN4, and older
		window["on" + evtType] = f;
	}
}
dtfxEx.reserve();

これが読み込まれると、ページの onload イベントで、dtfxEx.replaceOverlayImage() が実行されて画像が置き換えられる。


そして dtfxEx.js をロードするように書き換えた HTML がこれ。
ShootingFX-Overlay-Demo.html

<html>
<head>
<title>ShootingFX</title>
</head>
<body>
<h1>ShootingFX</h1>
<script src="http://dl.javafx.com/dtfx.js"></script>
<script src="dtfxEx.js"></script>
<script>
    // image's url, width, and height
    dtfxEx.overlayImage('metubusimark.gif', 384, 511);
    javafx(
        {
              archive: "ShootingFX.jar",
              draggable: true,
              width: 720,
              height: 720,
              code: "jp.fooami.shootingfx.Main",
              name: "ShootingFX"
        }
    );
</script>
</body>
</html>

画像の URL と幅、高さを指定して dtfxEx.overlayImage() を呼び出していて、dtfxEx.overlayImage() の中では画像が先読みされる。


そして下のリンクが、今回のサンプル。
http://www7b.biglobe.ne.jp/~fooami/shootingfx/ShootingFX-Overlay-Demo.html


どうだろう。。。「待たされている感」の度合いは。。。


ページの描画が始まって画像が置換されるまでの一瞬、コーヒーカップのアニメーションが表示されてしまうのが気になるかもしれないけど、Java コーヒーのおいしい香りを感じてもらうのに良いんじゃないですかね。w (これは Flash でも Silverlight でもなく Java ですよ。という意味)
どうしても気になるようならば、javafxString() 関数の結果のタグ文字列を取得して、タグを置換するカスタム javafx() 関数を書いたら良いと思う。


dtfxEx.js

dtfxEx.javafx = function(launchParams, appletParams) {
	var stringOutput = javafxString(launchParams, appletParams);
	if (stringOutput) {
		document.write(dtfxEx.replaceTagString(stringOutput));
	}
}


HTML

<script src="dtfxEx.js"></script>
<script>
    // image's url, width, and height
    dtfxEx.overlayImage('metubusimark.gif', 384, 511);
    dtfxEx.javafx(
        {
              archive: "ShootingFX.jar",
              draggable: true,
              width: 720,
              height: 720,
              code: "jp.fooami.shootingfx.Main",
              name: "ShootingFX"
        }
    );
</script>

こんな感じで。


またもしかすると、アプレットのプログラムサイズが小さかったり、アプレットを一度起動済みの状態でリロードした際、二度目の起動が早かったりすると、画像(自前の何らかのコンテンツ)が一瞬表示されてちらつくのが逆に邪魔になるかもしれない。
そんな場合は Timer を使って工夫するか、今回は dtfx.js のコピーライトを気にしてこういう形にしたけど、fxAppletStarted() を再定義して名前空間上で上書くなどの苦しい方法をしなければいけないかもしれない。


とどのつまり、本質的には Java Plug-in の更なる改良を待ちつつ、このような僕らができる方法で「待たされている感」を感じさせないような工夫をするしかないかなと。
そんなお話でした。