YouTube の Player API で YouTube 動画のモーダルスクリーンを実装する機会があったので、Bacon.js で実装してみたメモ。
全体のコードはこんな感じ:
(function (w, d, $, B) {
// YouTube Player API の読み込み関数
var youtubePlayerAPI = function (cb) {
var el = d.createElement('script'), topEl = d.getElementsByTagName('script')[0];
el.src = 'https://www.youtube.com/iframe_api';
w.onYouTubeIframeAPIReady = function () { cb(w.YT); };
topEl.parentNode.insertBefore(el, topEl);
};
// YouTube プレイヤのコンストラクタ関数
var createYoutubePlayer = function (YT, selectorId, videoId, cb) {
return new YT.Player(selectorId, {
videoId: videoId,
width: 320,
height: 180,
playerVars: {
controls: 1,
loop: 1,
rel: 0,
showinfo: 0,
wmode: 'transparent'
},
events: { onReady: cb }
});
};
// YouTube API 読み込み完了を待つストリーム
var youtubeAPIReady = B.fromCallback(youtubePlayerAPI);
// DOM 読み込み完了を待つストリーム
var contentLoaded = B.fromCallback($(d).ready);
// 上記 2 つの読み込み完了を待つストリーム
var ready = B.combineWith(function (content, api) { return api }, contentLoaded, youtubeAPIReady);
// 上記ストリームの読み込み完了タイミングで実行されるリスナ
ready.onValue(function (YT) {
var $open = $('#open'), $close = $('#close'), $layer = $('#layer'), $main = $('#main');
// 個別 YouTube 動画の読み込み完了を待つストリームバインダ
var videoBinder = function (selectorId, videoId) {
return function (sink) {
createYoutubePlayer(YT, selectorId, videoId, function (ev) {
sink(ev.target);
sink(new B.End());
});
};
};
// 上記バインダを利用したストリーム
var playerReady = B.fromBinder(videoBinder('player', 'DVwHCGAr_OE'));
// モーダルを開くリンクのクリック監視をするストリーム
var openClick = $open.asEventStream('click').doAction('.preventDefault');
// 上記開くリンクのトリガと YouTube 動画読み込み完了を待つストリーム
var open = openClick.combine(playerReady, function (click, player) { return player; });
// モーダルを閉じるリンクのクリック監視をするストリームプロパティ
var closeClick = $close.asEventStream('click').doAction('.preventDefault').toProperty(w.event);
// モーダル背景のクリック監視をするストリームプロパティ
var layerClick = $layer.asEventStream('click').toProperty(w.event);
// 上記 2 つどちらかのトリガを監視するストリームプロパティ
var closeOrLayerClick = closeClick.or(layerClick);
// 上記トリガと YouTube 動画読み込み完了を待つストリーム
var close = closeOrLayerClick.combine(playerReady, function (click, player) { return player; });
// 開く処理がトリガされた時に実行されるリスナ
open.onValue(function (player) {
$layer.css({ display: 'block' });
$main.css({ display: 'none' });
player.playVideo();
});
// 閉じる処理がトリガされた時に実行されるリスナ
close.onValue(function (player) {
player.stopVideo();
$layer.css({ display: '' });
$main.css({ display: '' });
});
});
})(window, document, jQuery, Bacon);
様々な非同期処理を、ストリームという時系列に値が push
されていく無限配列のようなオブジェクトに、Underscore.js のような配列操作で、様々な処理を書いていけるのが FRP っていうのが、今の認識。