[Flash] Flashムービーの読み込みと再生

最近は久しぶりに実務でFlashを触っています。
で、今になって「タイムラインベースのFlashムービーは読み込みの完了を待たずにストリーミング的に再生される」という、かなり基本的と思われる仕様を初めて知りました。

そしてそれは当然スクリプトからも制御できて、レアケースではありますがドキュメントクラスで

package 
{
	import flash.display.MovieClip;
	import flash.events.ProgressEvent;
	
	public class Main extends MovieClip
	{
		public function Main()
		{
			loaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler);
		}
		
		private function progressHandler(e:ProgressEvent):void 
		{
			// 80%以上読み込みが完了したら再生
			if (uint((e.bytesLoaded / e.bytesTotal) * 100 ) > 80) {
				loaderInfo.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
				gotoAndPlay(2);
			}
		}
	}
	
}

みたいにして、何割完了したら再生というような処理も書くことができます。
これってもしかして常識でしょうか…。

冷静になって考えてみると「ああ、そうかー」とも思うのですが、そもそも「読み込みが完了していないと再生されない(できない)」というのがまず頭にあったので、この仕様を知った(というか教えてもらった)時は、目からウロコでございました。うーん、どこで間違ったんでしょう…。

ちなみに上記は外部SWFを読み込み・再生する場合も同様のようですが、ローカルでの確認や読み込むSWFがキャッシュに存在するなど、瞬間的に読み込みが完了する状況では、ProgressEvent.PROGRESSのハンドラでうまくMainTimelineオブジェクト(Loader.content)にアクセスできず、nullが返ってきてしまいます。

イベント自体は配信されていますし、適当になんやらすれば回避は可能ですが、原因がわかっていない(というか調べていない)ので、なんとも。何かまた変な勘違いしている可能性も否定できませんけど(‘A’;)

思い込みと中途半端な理解は怖いなあ、というお話でした。


[JS] 外部ファイルの遅延読み込みを容易にする「PreloadJS」

Webコンテンツ制作において、JavaScriptで外部ファイルの読み込みを実現する機会は多々あり、そして中でも外部画像の遅延読み込みとその状況に応じたハンドリングについては、時代の流れもあって最近では特に需要が高まっていると感じます。が、実際、ブラウザ間の差異などの問題もあって、そのあたりをうまく実現しようとすると、結構面倒な処理が必要だったりします。

先日、Webにおけるインタラクティブコンテンツの制作を強力にサポートする「CreateJS」の公式サイトが公開されました。「CreateJS」はグラフィックス、アニメーション、サウンドなどの用途に特化した各種JavaScriptライブラリやツールによって構成されていますが、その中でも個人的にまず興味を持った(今すぐ使えそうだと思った)のが、外部ファイルの読み込みを容易に実現するためのライブラリである、表題の「PreloadJS」です。

「PreloadJS」はCSS、画像、JS、JSONなどなど様々なファイルの読み込みに対応しており、複数キューの管理とその一時停止/レジュームなどの便利な機能が色々と用意されいています。また、画像の読み込みに関しては、XMLHttpRequest level2(XHR2)によるローディングと、古くからあるタグを使ったローディングを環境によって内部的に切り替えるため、XHR2対応環境では(タグによるローディングでは実現できない)読み込みのより詳細な進捗状況を取得することができます。もしかしたら他に同等の性能を持つライブラリがあるのかも知れませんが、僕はこれが初見でしたので、この仕様に特に強く惹かれました。

機能自体は外部ファイルを読み込むというシンプルなものですから、使用方法も簡単。JSの基礎知識とドキュメントさえあれば迷うことは無さそうです。
以下は外部画像を読み込む場合のシンプルな例。

// 読み込む画像のリスト
var manifest = [
	{src: 'images/image0.jpg', id: 'img0'},
	{src: 'images/image1.jpg', id: 'img1'},
	{src: 'images/image2.jpg', id: 'img2'},
	{src: 'images/image3.jpg', id: 'img3'}
];

// 全体の読み込み進捗状況
var overallProgresssHandler = function( e ) {
	console.log( 'loading: ' + Math.floor( e.loaded ) * 100 + '%' );
};

// 全体の読み込み完了
var completeHandler = function( e ) {
	console.log( 'complete.' );
};

// ファイルごとの読み込み進捗状況
var fileProgressHandler = function( e ) {
	console.log( e.id + ' loading: ' + Math.floor( e.progress ) * 100 + '%' );
};

// ファイルごとの読み込み完了
var fileLoadHandler = function( e ) {
	console.log( e.id + ' is Loaded.' );
};

var preload = new PreloadJS();
preload.onProgress = overallProgresssHandler;
preload.onComplete = completeHandler;
preload.onFileProgress = fileProgressHandler;
preload.onFileLoad = fileLoadHandler;
preload.loadManifest( manifest );

↑PreloadJSインスタンスを作ってハンドラをセットし、loadManifest()に読み込みたい画像の配列を渡して呼び出す。画像ファイルごとにid(String)やdata(Object)といったメンバを持たせたObjectを格納すれば、各ハンドラに渡されるイベントオブジェクトからそれらの値を取得できます。

var manifest = [
	'images/image0.jpg',
	'images/image1.jpg',
	'images/image2.jpg',
	'images/image3.jpg'
];
~

↑特にデータを関連付ける必要がない場合は、単純にパスの文字列を格納した配列でもいいですし、

~
preload.loadFile( 'images/image0.jpg' );

↑ファイルがひとつだけならloadFile()にURLを文字列で渡せばOKみたいです。

自分自身まだ「ちょっと使ってみた」程度で、その他の細かいところなどについてはこれから把握していくところなのですが、遅延読み込みの諸問題を吸収しつつ多機能で扱いやすいAPIを提供してくれる「PrelaodJS」は自分が求めていたものに非常に近く、スピードとクオリティを求められる実務においてもとても重宝しそうだと個人的に思ったので、ありがたく使わせて頂こうと思っています。なんか偉そうですね。すいません。

とりあえず、今まで使っていた自作のなんか変なクラスみたいなやつは、もっとJSに対する深い知識が付いた頃に改めて作り直そうかな…。(‘A’;)


[JS/CSS] 背面にある要素のマウスイベントを発火する

ある要素の前面に何か別の要素をかぶせるように配置した時、背面にある要素のマウスイベントは基本的には反応しません。(前面の要素のみ)
当然といえば当然の話なのですが、デザインや仕様によっては、実際に構築するまでこの問題に気付きにくい場合があります。ありました。

下記のサンプルは、透過PNGでできた星画像をリンクボタンにかぶるように position:absolute で配置した例です。見た目の上ではかぶっていませんが、それは星PNGの背景が透過されているからで要素同士はしっかりかぶっているため、背面のリンクボタンにマウスを置いてもカーソルの形は変わりませんし、クリックしても無反応です。(ギリギリかぶっていないリンクボタンの上端と右端は反応する)

サンプル.01

これをどうにか前面の星PNGに邪魔されずにリンクボタンをマウスに反応させるようにする、というのがこの記事の本題。
一番簡単なのは、CSSで

.star { pointer-events: none; }

と書いてその名の通り前面になる要素をマウスに反応させなくする方法なんですが、ブラウザの対応状況から考えてとてもクライアントワークでは使えません。そもそもこのプロパティ自体、独自実装なのかな?

で、どうしたもんかと悩んでいたところで、以下のような記事を見つけました。

– 参考ページ: Forwarding Mouse Events Through Layers

前面要素のハンドラで、自身を一瞬消して(display:none)その間に document.elementFromPoint(x,y) から背面要素を取得する、という流れのようです。多分。
なるほど!という事で、早速泥くさい感じで試してみたのが以下のサンプル。

サンプル.02

とりあえず各主要ブラウザのほか、IE6やiPhoneでも動いているようです。(IE6は透過されませんが)
ただ、上記のサンプルが意図した通り動いているのはたまたまで、 document.elementFromPoint は実装によって挙動が異なるので、実際はもっとちゃんと書かないと破綻してしまう可能性大。iPhoneも拡大したりすると動かない。

– 参考ページ: 要素が画面上に見えているかどうかを調べる – by edvakf in hatena

記事タイトルが示す結果とは違うけど、とりあえず解決のメドがついたのでよしとしたいと思います。もっと根本的な解決方法をご存知の方がいましたら教えて下さいませ。

Flashなら target.mouseEnabled=false で済むから簡単なんだけどなあ…。


[jQuery] 単一ページのナビゲーション用プラグイン

※2012.03.11 : プラグインの不具合と記事の内容を少し修正しました。

全部1ページに収めちゃう系のサイトでもしかしたら使えるかもしれないjQueryプラグインを作ってみました。似たようなのが既にどこかにあるのかも知れませんが、僕は知らないのでいいのです。

“[jQuery] 単一ページのナビゲーション用プラグイン” の続きを読む


[CSS3] CSS3でパノラマVR

一時期によく見かけた気がするパノラマVRをiPhone/iPadに対応できるかみたいなことをふわっと質問され、「Three.jsさんならなんとかしてくれるんじゃないでしょうかー」とよく調べもせずに答えてしまいました。

ちょっと気になったので手持ちのiPhoneで実際に動かしてみたところ、ブラウザで開いた瞬間から明らかにダメそうな空気でしたのですぐにブラウザを閉じました。恐らく実行速度とかそもそもの問題でないかと思います(まあ、これもロクに調べてないんですが)。とりあえずすいませんでした。

まあそのような訳で、以前にウワサでiPhoneではCSS3が安定してるみたいなことを聞いたような記憶があったりなかったりしたので、3D部分はCSS3にやってもらう体で試作してみました。

サンプル(iPhone、iPad、または新しめのSafariのみ)Credit: La caverne aux livres by gadl on Flickr

修正すべき所はいろいろありますが、とりあえずそれっぽいものが出来ました。要素の3D化はCSS3で、マウスドラッグ(フリック)による視点の回転はJS(jQuery)でやってます。

がしかし、そもそもがThree.jsとかPapervision3Dといった3Dエンジンを利用するやり方とは大きく異なるので、仕様によっては実現がかなり難しくなりそう。

とりあえず上記のようにグルグル回すだけのものなら簡単だということは分かったので、今回はここまで。