ビデオと同期再生するHTMLアニメーション

この間、パララックス・ビューというsupercrazy Japanese music videoを楽しみながら、組み込みビデオと同期再生するHTMLアニメーションを、ビジュアルで作れないか、と思いました。Edge Animationでやってみたらそう難しくなかったので、やり方とソースコードをシェアします。素材とデモは下記ですが、まずはチュートリアル。

※ ビデオを作ってからコードをちっと更新しました。詳細は投稿の最後に。

リソースはこちら:

そして最後に、ビデオとEdge Animateの同期させるためのJS。新規プロジェクトで使うには、下記のJSを creationComplete イベントのイベントハンドラーに入れて、そしてプロジェクトのどこかにビデオコンテナを作って、そのシンボル名を video_holder にすれば、動くと思います。

/*
* 設定
*/

// Youtubeビデオのコード
var video_code = 'k6wf9QjWo8w';

// 組み込みビデオのサイズ
var vid_w = '560';
var vid_h = '315';

// iFrameを使うかembedプレーヤを使うか
var useIFrame = false;

// site URL - iframeプレーヤの場合に必須
var site_url = 'https://fenomas.com';

// iframeプレーヤ版をEdge Animateからプレビューする場合にこれ:
var site_url = '127.0.0.1:54321';

/*
* REST OF SCRIPT BELOW
*/

// ページのstyle設定
$('body').css('backgroundColor', '#000')

// youtube video リファレンス
var player = undefined;

// ビデオの再生状態が変わった時のハンドラー
window.onVideoState = function(status) {
// status will be 1 when playing, 2 when paused
var time = player.getCurrentTime(); // in seconds
if (status==1) {
// video is playing
sym.play(time*1000);
} else if (status==2) {
// video paused
sym.stop(time*1000);
}
}

// ユーザが手動でビデオをシークすると、Youtube APIが
// なぜか全くイベントを発生しないため、ビデオの
// getCurrentTimeをポーリングしないといけない :(
function updatePlayback() {
if (player && player.getCurrentTime) {
var t = player.getCurrentTime() * 1000;
var state = player.getPlayerState();
var dt = sym.getPosition() - t;
// match up times if discrepancy exceeds some tolerance
var tolerance = 500; //ms
if (Math.abs(dt) > tolerance) {
if (state==1) { sym.play(t); }
else { sym.stop(t); }
}
}
requestAnimationFrame(updatePlayback);
}
requestAnimationFrame(updatePlayback);

/*
* EMBED VERSION
* Youtubeのembedプレーヤ(Flash制)を使う場合の処理
* iframeプレーヤよりシンプルだけどモバイルが非対応
*/

if ( ! useIFrame) {

// youtube API から発生する初期イベント
window.onYouTubePlayerReady = function() {
player = document.getElementById('player');
// 上記のイベントハンドラに渡す
player.addEventListener("onStateChange", "onVideoState");
}

// embedプレーヤのタグを作る
var params = '?version=3&enablejsapi=1';
var embedScript = '<object width="'+ vid_w +'" height="'+ vid_h +'"><param name="movie" value="//www.youtube.com/v/'+video_code+params+'"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed id="player" src="//www.youtube.com/v/'+video_code+params+'" type="application/x-shockwave-flash" width="'+ vid_w +'" height="'+ vid_h +'" allowscriptaccess="always" allowfullscreen="true"></embed></object>';

// 'video_holder' シンボルのHTMLエレメントを取得
// 中身をembedタグで上書き
sym.$('video_holder').html( embedScript );

} // end embed insertion

/*
* IFRAME VERSION
* iframeプレーヤの組み込みビデオの処理
* クロスサイト制限をまたいでEventのやりとりをするので少々複雑。
*/

if (useIFrame) {

// Youtube APIの初期イベント
window.onYouTubeIframeAPIReady = function() {
// プレーヤリファレンス
player = new YT.Player('player', {
events: {
// 再生状態変更イベント
'onStateChange': function(e) {
window.onVideoState(e.data);
}
}
});
}

// iframeタグを作って、ページに挿入
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

// iframe パラメータを設定。
// サイトのURLも指定しないとJSイベントが届かない
var params = '?version=3&enablejsapi=1&origin=' + site_url;

// iframeタグを作る
var embedScript = '<iframe id="player" width="'+ vid_w +'" height="'+ vid_h +'" src="https://www.youtube.com/embed/' + video_code + params + '" frameborder="0"></iframe>';

// 'video_holder'の中身を上書き
sym.$('video_holder').html( embedScript );

} // end iFrame insertion

以上!なにか格好良いの作ったら教えてください。

ちなみに、今回のチュートリアルビデオは日本語の音声の変わりに、字幕を付けてみました。吹き替えより分かりやすいか、わかりにくいか、感想があればコメントください。(タイミングの調整がものすごく時間かかったので二度としない気がしますが・・)

※ 更新!

(2014/7/9)  上記のJSコードを2箇所を更新しました。ビデオを更新していませんが、プロジェクトのソースを拾えば動くかと思います。詳細情報:

  1. プレビュー用のアドレスをEdge AnimateのCC 2014リリースに合わせて、「localhost:54321」から「127.0.0.1:54321」に変更
  2. requestAnimationFrameのループで、ビデオのcurrentTimeをポーリングするように変更。理由は、YoutubeのHTMLプレーヤーが、ユーザが手動にビデオをシークする時にイベントを全く発生しないため、ビデオとアニメーションがずれてしまうこと。(バグではなくて意図的らしい・・)