この記事は、http://css-tricks.com/ の Chris Coyier の許可を得て、翻訳しています。一部変更して翻訳している部分もある場合があります。オリジナルの記事はここよりご覧いただけます。
CSSでサウンド再生する方法をググると、大抵以下の様な検索結果が出るかと思います:
- Counter Strike: Sourceに関する何やら。
- play-duringやcue-beforeに関する何やら。 こちらは一見使えそうに見えますが、実際には古いブラウザでメニューアイテムをロールオーバーした時に奇妙な音を立てるための機能だけでなく、聴覚スタイルシート(アクセシビリティとかスクリーンリーダー等)をサポートするためのものです。
サウンドもまたデザインの一部なのだから、CSSでコントロール出来るべきだと思うのですが、残念ながらまだそこまでは至っていません。指定したエリアをマウスオーバーした時にサウンドが再生されるようにするには、HTML5かFlashに頼らなければなりません。でもこれを読んでいる皆さんはFlashを使うよりも、HTML5を使って再生する方が興味があるはずです。
HTML5では<audio>要素(Firefox 3.5+、 Chrome 3+、 Opera 10.5+、 Safari 4+、 IE 9+で対応)を使って、サウンド再生することが出来ます。ブラウザのサポートを最大限活用するために、MP3ソース(WebKitとIE)とOGGソース(ForefoxとOpera)の両方を使ってみましょう。
<audio> <source src="audio/beep.mp3"></source> <source src="audio/beep.ogg"></source> Your browser isn't invited for super fun audio time. </audio>
上記のコードを丸々そのままページに記述したとしても、何も見えないし聞こえません。小さなプレイヤーを表示させたいなら、コントロール属性(<audio controls>)を使う必要があることを覚えておきましょう。音源だけ再生させてプレイヤーを表示させたくない場合は、オートプレイ属性(<audio autoplay>)が必要となります。
今回の我々のゴールは、メニュー項目など特定の要素の上にマウスオーバーさせると、サウンドが再生されるということです。しつこくなりますが、残念ながらCSSで<audio>要素に指示を与えることは出来ませんので、JavaScriptを使用することになります。JavaScriptを使ってサウンド再生するには以下のようなコードになります:
var audio = document.getElementsByTagName("audio")[0]; audio.play(); // or with an ID var audio = document.getElementById("mySoundClip"); audio.play();
イベントの取り扱いと選択が簡単になるので、jQueryを使ってみましょう。
var audio = $("#mySoundClip")[0]; audio.play();
ではこれに、指定した要素をマウスオーバーした時にサウンド再生するようにコードを加えます:
var audio = $("#mySoundClip")[0]; $("nav a").mouseenter(function() { audio.play(); });
その他の方法
Goodfootモバイルアプリのティーザーページでは「Available on the App Store」をホバーすると変な唸り声が聞こえるという、似たようなテクニックが使われています。このページではアイコンがホバーされる度に、DOMに新しいオーディオ要素が注入されるようになっています:
$("#speak").mouseenter(function(){ $("<audio></audio>").attr({ 'src':'audio/'+Math.ceil(Math.random() * 5)+'.mp3', 'volume':0.4, 'autoplay':'autoplay' }).appendTo("body"); });
同様にOGGもサポートするには、もう少し改善しないといけないところもありそうですね。W3Cのスペックでも他のどこでも見かけたことがないので、ボリューム属性がどうなっているかはちょっとハッキリとは分かりません。こちらはジェフリー・ウェイさんによる修正版です:
function addSource(elem, path) { $('<source>').attr('src', path).appendTo(elem); } $("#speak").mouseenter(function(){ var audio = $('<audio />', { autoPlay : 'autoplay' }); addSource(audio, 'audio/'+Math.ceil(Math.random() * 5)+'.mp3'); addSource(audio, 'audio/'+Math.ceil(Math.random() * 5)+'.ogg'); audio.appendTo('body'); });
きちんと動作するように見えるし、言ってみればそれが最低条件なので、このアプローチの仕方はとてもいいと思います。一度サウンド再生されると、キャッシュされて再生速度が上がるようなのですごくいいですね。
他に考えうる方法としては、3つのオーディオ要素全てをページに記述してしまうことでしょうか。
<audio preload="auto" id="sound-1" > ... src & fallback ... </audio> <audio preload="auto" id="sound-2" > ... src & fallback ... </audio> <audio preload="auto" id="sound-3" > ... src & fallback ... </audio>
それから、この内の1つがランダムで選択されるようにします:
$("#speak").mouseenter(function() { $("#sound-" + Math.ceil(Math.random() * 3))[0].play(); });
トライアルと問題点:サウンドオーバーラッピング
私の元々のアイデアは「ナビゲーションメニュー等をホバーすると小さなクリック音が再生される」というものでしが、すぐに問題点が浮かび上がりました:.play( )をトリガーするメニューアイテムを、サウンド音が終わるよりも断然早くホバー出来てしまうということです。単体のオーディオ要素は自分自身のサウンドをオーバーラッピング再生させることは出来ません。サウンドが再生し終わるまでは.play( )のリクエストを無視し続けてしまいます。
この対策として、まず.pause( )でサウンド再生を停止させて再度.play( )させることを思い付きました。けれどあまりうまくいきませんでした。きちんと停止してはくれるのですが、その後再び再生されないことが何度もあったのです。
一番スムーズに行く方法としては、オーディオ要素を全てのメニューアイテム用に複製することでした。こうすればメニューアイテム1つ1つがオーディオ要素を所有しているので、サウンド再生もオーバーラップも可能になります。
$("nav a") // loop each menu item .each(function(i) { if (i != 0) { // only clone if more than one needed $("#beep") .clone() .attr("id", "beep-" + i) .appendTo($(this).parent()); } $(this).data("beeper", i); // save reference }) .mouseenter(function() { $("#beep-" + $(this).data("beeper"))[0].play(); }); $("#beep").attr("id", "beep-0"); // get first one into naming convention