読み込み完了前のエラーを回避する
前のページまでで必要な修正はほぼ終わりましたが、このままだと映像の準備ができていない場合にエラーで処理が停止してしまうことがあります。ページの読み込みは完了していても、映像の再生準備/表示準備はできていない場合があり、その場合にアクセスが発生するとエラーになってしまうのです。
特に、Appleスライダを利用する場合、イベントハンドラ内に映像へアクセスする処理を書いているとエラーになります。
そこで、再生可能になった時点でAppleスライダを構築するように変更します。映像の再生が可能になったらcanplayイベントが発生しますので、canplayイベントが発生してからAppleスライダを構築すればいいわけです。実際のプログラムは以下のようになります。
window.addEventListener("load", function(){ var videoEle = document.getElementById("myVideo"); videoEle.addEventListener("canplay", setUp, true); }, true);
以上で完成、と言いたいところですが、もう1点だけうまく動作しない機能があります。映像を全画面で再生する「フルスクリーンボタン」です。Dashboardの制限上、フルスクリーンでの映像再生はできませんので、HTMLレベルで削除するのがベストですが、今回はブラウザーでもDashboardでも同じHTMLが利用できるように、Dashboard上で動作している場合だけフルスクリーンボタンを非表示にします。
JavaScriptがDashboard上で動作している場合は、windowオブジェクト内にwidgetというオブジェクトが生成されます。Safariには存在しないオブジェクトですので、widgetオブジェクトが存在していた場合はDashboard上で動作しているとみなすことができます。以下のスクリプトでは、Dashboardで実行されている場合にフルスクリーンボタンを非表示にしています。
window.addEventListener("load", function(){ var videoEle = document.getElementById("myVideo"); videoEle.addEventListener("canplay", setUp, true); if (window.widget){ $("#fsButton").hide(); } }, true);
以上で、Dashboard版「HTML5 Video Player」の完成です。
◆
5回にわたってMac OS XのDashboardウィジェットについて説明しましたが、Dashboardウィジェットにはまだまだ多くの機能があります。UNIXコマンドを利用することもできますし、Objective-Cでプラグインも作成できます。「Mac OS Xでアプリケーションを作るのは難しそうだ」という人は、Dashboardウィジェットから始めてみてはいかがでしょうか。Dashboardのウィジェットができたら、今度はHTMLアプリケーションとしてiPhoneやAndroidでも動作するようにしてみるのもよいでしょう。
■Dashboard用HTML5 Video Player完成版[HTML]
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>HTML5 Dashboardビデオプレイヤー</title> <link rel="stylesheet" href="css/ui-lightness/jquery-ui-1.8.9.custom.css" type="text/css" media="all"> <link rel="stylesheet" href="css/main.css" type="text/css" media="all"> <script type="text/javascript" src="js/jquery-1.5.min.js"></script> <script type="text/javascript" src="js/jquery-ui-1.8.9.custom.min.js"></script> <script type="text/javascript" src="/System/Library/WidgetResources/AppleClasses/AppleSlider.js" charset="utf-8"></script> <script type="text/javascript" src="js/player.js"></script> </head> <body> <h1>HTML5 Dashboardビデオプレイヤー</h1> <div id="myPlayer"> <video id="myVideo" src="movie/sample.mov" preload width="768" height="432"></video> <div id="control"> <img src="images/play.png" id="playButton" width="32" height="32"><!-- --><img src="images/volume.png" id="volumeButton" width="32" height="32"><!-- --><img src="images/fullscreen.png" id="fsButton" width="32" height="32"> <div id="ctime">00:00/00:00</div> </div> <div id="slider"></div> <div id="volumeSlider"></div> <div id="speedSlider"></div> <div id="playList"> <img src="thumbnail/fireworks.jpg" width="192" height="108" title="fireworks"><br> <img src="thumbnail/hachibuse.jpg" width="192" height="108" title="hachibuse"><br> <img src="thumbnail/rice.jpg" width="192" height="108" title="rice"><br> <img src="thumbnail/sample.jpg" width="192" height="108" title="sample"><br> <img src="thumbnail/suimon.jpg" width="192" height="108" title="suimon"><br> <img src="thumbnail/tatsuno.jpg" width="192" height="108" title="tatsuno"><br> </div> </div> </body> </html>
■Dashboard用HTML5 Video Player完成版[CSS]
* { margin: 0; paddig: 0; } body { background-image: url(../Default.png); } h1 { margin: 0; padding: 0; color : #101030; background-color : #c0c0c0; font-size : 14pt; font-weight: bold; font-family: Tahoma; width : 768px; height: 25px; } img, video, #myPlayer, #control { top: 0px; left: 0px; margin: 0px; padding: 0px; line-height:0px; } #control { position: relative; top: -4px; background-image:url(bar.png); width: 768px; height:32px; overflow: hidden; } #slider { position: relative; top: -54px; margin: 0px 0px 0px 5px; width: 755px; opacity : 0.75; font-size: 9px; line-height: 100%; } #volumeSlider { position: relative; top: -53px; left: 70px; margin: 0px 0px 0px 5px; width: 100px; opacity : 0.75; font-size: 9px; line-height: 100%; } #speedSlider { position: relative; top: -75px; left: 300px; margin: 0px 0px 0px 5px; width: 100px; opacity : 0.75; font-size: 9px; line-height: 100%; } #fsButton { position: relative; left:672px; } #ctime { position: relative; left:190px; top: -16px; font-size:11pt; font-weight: bold; font: vardana; } #slider.ui-widget-content { background-image: url(red.png); } #playList { position: relative; left: 780px; top: -554px; width: 210px; height: 480px; border:2px solid gray; overflow: scroll; } #playList img:hover { opacity: 0.7; }
■Dashboard用HTML5 Video Player完成版[JavaScript]
window.addEventListener("load", function(){ var videoEle = document.getElementById("myVideo"); videoEle.addEventListener("canplay", setUp, true); if (window.widget){ $("#fsButton").hide(); } }, true); function setUp(){ var videoEle = document.getElementById("myVideo"); videoEle.removeEventListener("canplay", setUp, true); // 再生時間を指定するスライダーの処理 var slider1 = new AppleHorizontalSlider(document.getElementById("slider"), function(currentValue){ videoEle.currentTime = currentValue * videoEle.duration; videoEle.pause(); $("#playButton").attr("src", "images/play.png"); } ); // ボリュームスライダーの処理 var slider2 = new AppleHorizontalSlider(document.getElementById("volumeSlider"), function(currentValue){ videoEle.volume = currentValue; } ); slider2.setValue(0.5); // 可変速スライダーの処理 var slider3 = new AppleHorizontalSlider(document.getElementById("speedSlider"), function(currentValue){ var spd = currentValue * 2; if (spd < 0.5) { spd = 0.5; } videoEle.defaultPlaybackRate = videoEle.playbackRate = spd; } ); slider3.setValue(0.5); // 再生ボタンの処理 $("#playButton").click(function(){ if (videoEle.paused){ videoEle.play(); // 再生する $(this).attr("src", "images/pause.png"); }else{ videoEle.pause(); // 一時停止する $(this).attr("src", "images/play.png"); } }); // ミュートボタンの処理 $("#volumeButton").click(function(){ if (videoEle.muted){ videoEle.muted = false; // 通常の音量にする $(this).attr("src", "images/volume.png"); }else{ videoEle.muted = true; // ミュートする $(this).attr("src", "images/mute.png"); } }); // フルスクリーンモードにするボタン $("#fsButton").click(function(){ try{ videoEle.webkitEnterFullscreen(); // フルスクリーンモード }catch(e){} }); // 映像が再生されている時の処理 videoEle.addEventListener("timeupdate", function(){ var cTime = videoEle.currentTime; // 現在再生されている時間 var dTime = videoEle.duration; // 映像の長さ //slider1.setValue(cTime / dTime); document.getElementById("ctime").innerHTML = toTimeStr(cTime)+"/"+toTimeStr(dTime); }, true); // 映像の再生が終了した時の処理 videoEle.addEventListener("ended", function(){ $("#playButton").attr("src", "images/play.png"); videoEle.currentTime = 0; // Dashboard用に追加 videoEle.pause(); // Dashboard用に追加 }, true); // ボリュームが変化した時の処理 videoEle.addEventListener("volumechange", function(){ $("#volumeSlider").slider("value", videoEle.volume*100); }, true); // 時間形式に変換 function toTimeStr(n){ var m = "00"+Math.floor(n / 60); // 秒を分にする var s = "00"+Math.floor(n % 60); // 秒のみにする m = m.substr(m.length-2, 2); // 2桁の数値にする s = s.substr(s.length-2, 2); // 2桁の数値にする return m+":"+s; } // --------------- サムネールがクリックされた時の処理 ----------------- $("#playList img").click(function(){ var videoName = $(this).attr("title"); // title属性にファイル名が入っているので読み出す videoEle.src = "movie/"+videoName+".mov"; // 読み込むファイル名(URLも可)を指定 videoEle.load(); // 映像データを読み込み $("#playButton").attr("src", "images/play.png"); // 再生ボタン再設定 $("#slider").slider("value", 0); // 0秒にリセット }); }