Presentation APIを使ってみる
今作っているFusumaというスライドツールにPresentation APIを追加してみました。
Presentation API とは?
ステータスは 勧告候補 で、今現在はchromeのみ実装されています。
このAPIは、KeynoteやPowerPointでプレゼンテーションモードにすると他のディスプレイに出力できるようになる機能をブラウザで実現できます。
ディスプレイの他にも、Chrome CastやAirPlay等にも映し出すことも可能です。
とりあえずディスプレイが一枚繋がっているかネットワーク以下にChrome Cast等があれば使えます。
デモ
- chromeで上記のページに行く
- 左下のハンバーガーメニューを押して、ロケットアイコンを選択する
- 下のスクリーンショットのように表示されるのでディスプレイを選択する(おそらく1が自身になる気がする)
- 現在のページがコントローラーになって、選択したディスプレイにフルスクリーンでスライドが表示される
あとは、コントローラー側で矢印キーを使って操作すれば、レシーバー側(フルスクリーンになったスライド側)のスライドも変わると思います。
いままではどのようにやっていたか?
今までもこのように別画面でフルスクリーンにスライドを表示する実装は出来ていました。
これを実現するためには、localStorageでページ情報と挿入イベントのリッスンを行います。
大きな違いとしては、自動で他のディスプレイにフルスクリーンで表示できる点が大きいです。
今までの場合は、window.open
でレシーバー側を表示し、それを他のディスプレイに移し、フルスクリーンにするという手順が必要でした。
これからは、対応していなければlocalStorageの方を使う実装となりました。
また、Presentation APIで表示されたレシーバーからはlocalStorageは参照できません。
Chromeのサンプルコードは以下を参照
コード
Presentation API と localStorage をまとめたコードは以下を参照
コントローラー
接続処理
presentation api
async function connect() { return new Promise((resolve, reject) => { const presentationRequest = new PresentationRequest(['?presenter=view']); navigator.presentation.defaultRequest = presentationRequest; presentationRequest.addEventListener('connectionavailable', (e) => { const presentationConnection = e.connection; resolve(presentationConnection); // presentationConnectionからternimate以外の操作を行う }); presentationRequest.start().catch((err) => reject(err)); }); }
localStorage
localStorageなので特になし
メッセージング
presentation api
presentationConnection.send(JSON.stringify({ page: 1 }));
localStorage
localStorage.setItem( 'page', JSON.stringify({ date: Date.now(), page: 1 }) );
レシーバー
接続処理
presentation api
接続という処理はなく、イベントを登録していく感じとなります。
navigator.presentation.receiver.connectionList
に接続されているコネクションのリストが入っています。
function addEvent(name, cb) { if (navigator.presentation && navigator.presentation.receiver) { navigator.presentation.receiver.connectionList.then((list) => { list.connections.forEach((connection) => { connection.addEventListener(name, cb); }); list.addEventListener('connectionavailable', (event) => { cb(event.connection); }); }); } } addEvent('close', (e) => { console.log(e); });
localStorage
localStorageなので特になし
メッセージング
presentation api
navigator.presentation.receiver.connectionList.then((list) => { list.connections.forEach((connection) => { connection.addEventListener('message', (e) => { const page = JSON.parse(e.data).page; }); }); });
localStorage
window.addEventListener('storage', (e) => { if (e.key === 'page') { const page = JSON.parse(e.newValue).page; window.slide.bespoke.slide(page); } });
問題点
- ライブリロードされると、レシーバーが破棄されるため毎回開き直し
- レシーバー側では、ショートカットキーが使えず、右クリックから検証を押さないとインスペクターが開けない
- シークレットウィンドウだと、起動はするがメッセージが送れない
UnknownError: Mismatch in incognito status: request = 1, response = 0
とりあえずデバッグが大変めんどくさかったです。
今後
Presentation APIとlocalStorageの互換性があるライブラリを書くかもしれません。
しかし、Chrome Castなどはブラウザの域から外れ、localStorageでは対応できないため完璧な対応はできないと思います。
message周りを抽象化したいなーって実装してて感じました。
すごい便利な機能なので早くほかのブラウザにも入ってほしいと思っています。