WebRTC(PeerJS)とPolymerを使ったデスクトップ画面共有アプリを作ってみた。
itinao
10:00
Chrome拡張
,
JavaScript
,
Knockout.js
,
PeerJS
,
Polymer
,
Web Components
,
WebRTC
0 件のコメント
表題の通りですが、WebRTC(PeerJS)とPolymer(全然凝ってないけど)を使ったデスクトップ画面共有アプリを作ってみました。
これで何ができるかっていうと、
画面上で起動しているアプリケーションをリアルタイムに画面共有ができます。
図. こんなイメージ。 |
共有する側(図の左側)はChrome拡張機能をインストールする必要があります。
これは、「Desktop Capture API」を使いたかった為です。
chrome.desktopCapture
⇒ https://developer.chrome.com/extensions/desktopCapture
共有を開始すると共有用URLが生成されるので、
こちらを閲覧する側(図の右側)に教えてGoogle ChromeでURLを開くだけでリアルタイムに操作内容が閲覧できます。
http://screen-share.itinao.asia/
◆ Chrome拡張機能インストール
https://chrome.google.com/webstore/detail/desktop-capture-share/albmbgococdgmmoajacmcbfcidhbinlh
使った技術
Chrome拡張機能側Knockout.js
⇒ 状態によって表示するものが切り替わるので、フレームワークを使用しました。
無くても書けましたがソースが汚くなっていくー。。って感じだったので導入。
共有されるURL側
Web Components(Polymer)
⇒ 試しに使ってみました。
peer-videoタグを作ったぐらいです。
(属性でホストとかURLを指定すれば勝手にシグナリングしてvideoタグで再生してくれるやつ。)
共通
WebRTC(PeerJS)
⇒ ほんと楽にシグナリングしてくれるんで便利。
(PeerJSサーバーも立てました。)
gulp
⇒ タスクランナーはgulpを使ってみました。
Polymerを使ったので、gulp-vulcanizeを導入してロードファイル数を下げてみる。とかやりました。
まとめ。
これは、技術的にできるなーって思ってから1〜2日で動作の確認までできました。
見た目整えて、gulp走らせるようにして、、ってやってたら1週間以上かかってのリリースになりました。
あと、
RecordRTCを使って録画機能も付けてみようかなーと考え中です。
録画したWebMファイルを接続相手に送信。。なんてこともできちゃいますね!
なんか色々出来て面白い。
以上!
Shadow DOMについて。
今回はShadow DOMの「content」と「select="セレクタ"」と関連するセレクタらへんの話しにします。Polymerを勉強したての頃は「?」な部分が多く、
何がよく分からないのかなーと色々と調べたところ、
Web Components(特にShadow DOM)の理解が足りなかったんだなってことが分かりました。
(きっといきなりPolymerを覚えると、jQueryは触れるけどJavaScriptよくわかんないみたいな感じ?
なので、今回はShadow DOMがテーマです。
今回のサンプルコードは下記のAPI使っています。
HTML Templates
Shadow DOM
筆者の実行環境は【Google Chromeのバージョン 37.0.2062.124】です。
(Shadow DOM は Chrome 35 以降で利用可能らしいです)
Shadow DOMって?
以前の記事「Web ComponentsとPolymerをさわってみる。」にも記述しましたが、
【DOM要素のレンダリング結果を、その要素が持つDOMのサブツリーとは独立に与える仕組み。】です。
ページの他の部分から切り離され、CSSの記述を間違って同じクラス名つけちゃっても、個々に解釈されるため大丈夫。って感じです。
その他にも色々機能があるので、サンプルコードで説明していきます。
サンプルコードで見てみる
基礎編
1. 通常パターン
DIVにスタイルをあてると、当然こうなる。
2. Shadow DOMを使って、見た目の情報を隠す。
要素に生えている「.createShadowRoot()」を実行すると、見た目の情報はShadow DOMを見に行くようになる。
(今回の場合、Shadow DOMに何も要素が入っていないので、外側で定義されているborder等のみ表示されている。
3. Shadow DOMに要素を入れていみる。
「.createShadowRoot()」で生成されるオブジェクトに対して、要素を入れると表示される。
templateタグで要素を作成し、JavaScriptでAppendする。
Content編
5. contentタグを使って、「<div id="test1">test1</div>」で記述してた「test1」というテキストを復活させる。
上記のサンプルで表示されなくなっていた「test1」(言い換えると、Shadow DOMを用いてる要素のinnerHTML)は、contentというタグを挿入ポイントとし、代入してくれる。
6. contentタグを複数使ってみる。
contentタグに「select="セレクタ"」を指定することで、挿入する内容を指定することができる。(指定していない場合も混合できる)
※注意点: selectは直下にある要素しか選択することができない。こういう指定はできないってことです⇒[select="ul li"]
スタイル編
7. 【:hostセレクタ】Shadow DOMの「ページの他の部分から切り離される」というスタイルの当て方をする。
DIVにスタイルを当てていましたが、これをTemplate内へ移動します。
そして、「:host」というセレクタを指定することで、Shadow DOMのホスト要素に対して、Shadow DOMからスタイルを当てることができます。
また、「:host(.hogehoge)」と指定すると、ホスト要素にhogehogeクラスが指定されてた場合に適用されます。
(外側からスタイルを指定している場合は、外側が優先されるので注意。)
8. 【:host-contextセレクタ】Shadow DOMのホスト要素の状態でスタイルを切り替える。
id="test2"に対してもレイアウトを適用したいので、shadowDomTestという関数を作り、test1とtest2の両方にShadow DOMを適用しました。
しかし、サンプルのソースコードではtest2のみスタイルがあたっています。
これはtest2の要素の親にclass="hogehoge"と指定した為で、
「:host-context(.hogehoge)」と指定すると、Shadow DOMのホスト要素の親要素にhogehogeクラスが指定されてた場合に適用されます。
(:hostより優先度が高いので注意。)
9. 【::shadowセレクタ】Shadow DOMの外側からスタイルを当てる。
template要素内に「<span>test0</span>」を追加し、外部から「#test2::shadow span」でスタイルを指定すると、「<span>test0</span>」に適用されます。
10. 【::contentセレクタ】contentタグで挿入された要素に対してスタイルを当てる。
id="test1"の子要素の「<span hoge2>test1_2</span>」は、template要素内のcontentタグに代入されます。
content内の要素に対してスタイルを当てる場合は「::content [hoge2]」を指定します。
11. 【/deep/セレクタ】 Shadow DOMのカプセル化を突き破ってスタイルを指定する。
「body /deep/ span」このように指定することで、「::shadow」と書いていた部分もスタイルの適用可能箇所となります。
(Shadow DOM以外の要素にも適用されます)
まとめ
このサンプルコードに記載したことを把握しておけば、PolymerのDemoコードも少しは読めるようになるはず。。と思います。が、Polymer独自の拡張部分である、Layout属性だったり、{{ 値 }}みたいな双方向バインディングの仕組みをもってたりするので、
それはそれでドキュメントをシッカリ読まないと理解できないかもしれません。
以上!
Web Componentsを試してみる
【Web ComponentsとPolymerをさわってみる。】の続きですが、今回はWeb Componentsで軽くサンプル作りつつポイントを書いていきたいと思います。
Polymerについては触れないです。
今回サラっと触っているのが、下記のAPIです。
Custom Elements
HTML Imports
HTML Templates
Shadow DOM
ちなみに、筆者の実行環境は【Google Chromeのバージョン 37.0.2062.120】です。
サンプルコード
このコードは、ホバーすることで色の変わる【x-sample】というタグを作っています。
そして、コード中にある、8つの【※注目※】については下部で説明します。
- 注目1
:unresolved
というCSSの擬似クラスが適用されるのは、Custom Elementsが未定義の場合。
サンプルコードを実行すると、<x-sample>は適用されておらず、<x-sample2>は適用されている。
<sample>は命名規則からズレているからか、適用されず。
「:unresolved」の使いドコロは、未定義状態の要素が見えてしまうことによって起こる表示のチラツキを抑えるとかになります。
というCSSの擬似クラスが適用されるのは、Custom Elementsが未定義の場合。
サンプルコードを実行すると、<x-sample>は適用されておらず、<x-sample2>は適用されている。
<sample>は命名規則からズレているからか、適用されず。
「:unresolved」の使いドコロは、未定義状態の要素が見えてしまうことによって起こる表示のチラツキを抑えるとかになります。
- 注目2
<link rel="stylesheet" href="sample.css">
でcssを読み込もうとしているが、読み込みされない。
http://www.w3.org/TR/shadow-dom/#inert-html-elements
直書きすれば問題はないが、それでは色々と辛いので下記のような手法がある。
Vulcanizeで減らすHTML ImportsのHTTPリクエスト
また、Polymerの場合は良しなにインライン展開してくれるらしい。
でcssを読み込もうとしているが、読み込みされない。
http://www.w3.org/TR/shadow-dom/#inert-html-elements
図. 通信さえ走らない |
Vulcanizeで減らすHTML ImportsのHTTPリクエスト
また、Polymerの場合は良しなにインライン展開してくれるらしい。
- 注目3
ここでdocumentを参照すると、インポートした側の参照になってしまう。
インポートされる側を参照できるようにしておく。
インポートされる側を参照できるようにしておく。
- 注目4〜7
4〜7で指定しているコールバックの実行タイミングは下記です。
実際に、Developer Toolsのconsoleから実行してみればよく分かると思います。
実際に、Developer Toolsのconsoleから実行してみればよく分かると思います。
// 要素が作成されたタイミング
var sample = document.createElement('x-sample');
⇒ createdCallback
// 要素の属性が変更されたタイミング
sample.id = "sample";
⇒ attributeChangedCallback
// 要素がDOMツリーへ挿入されるタイミング
document.body.appendChild(sample);
⇒ attachedCallback
// 要素がDOMツリーから削除されるタイミング
document.body.removeChild(sample);
⇒ detachedCallback
- 注目8
Custom Elementsで使用できるタグの名前には、ハイフンが必須です。
下記の場合にはこんなエラーに。
下記の場合にはこんなエラーに。
document.registerElement('sample')
SyntaxError: Failed to execute 'registerElement' on 'Document': Registration failed for type 'sample'. The type name is invalid.
終わりに
Web Componentsネタは、ちょいちょい書くかもです!以上!
JavaScriptでファイルを即時ダウンロードさせる処理を作ってみた
表題のとおりですが、詳細はこんなんです。
1. WebRTCでPeer to Peerする2. 自身のブラウザで画像ファイルとかをドラッグ&ドロップする
3. 自身のブラウザでそのファイルを送信処理をする
4. 接続している相手のブラウザで勝手に即時ダウンロード
(ブラウザの設定で、保存ディレクトリを固定だったら勝手に保存される)
↑これ、結構ひどいことできますよね。。
ということで、
この記事はWebSocketやWebRTCを使って他人と接続し、他人からのPUSHを受け取れる状態になっておくことが前提になります。
で、どうやるの?
自身がPUSHして、相手側の受信イベントのコールバックでこんなスクリプトを実行するだけです。// dataの内容は下記
var filename = data.filename;
var blob = new Blob([data.file], {type: data.mimeType});
// ファイルを自動でダウンロードするこの受信処理で、PDF・PNG・GIFは問題なくダウンロードできたのは確認しました。
var element = document.createElement("a");
element.download = filename;
element.href = window.URL.createObjectURL(blob);
element.click();
これだけではアレなので、筆者の場合…↓
筆者の場合 その1(送信ファイルの準備)
上記コードの、「data」の内容は下記です。ドラッグ&ドロップできるdivを用意し、ファイルをドロップする。
そのdropイベントで、受け取ったファイルの内容が「data」です。
dropイベントのコールバック
var files = event.dataTransfer.files;
for (var i = 0, l = files.length; i < l; i++) {
var file = files[i];
// ★★★data★★★↓
var data = {
file: file,
filename: file.name,
mimeType: file.type
};
}
筆者の場合 その2(送信処理)
筆者が作ってみた環境では、PeerJSを使いました。前回はライブラリ使わずに色々やりましたが、これはめっちゃ便利でした…。
dataConnection.sendってやるだけで、JSONやバイナリBlobs、ArrayBuffersを良しなに送れます。
(PeerJSはBinaryPackシリアライゼーションフォーマットを利用しているらしい)
http://nttcom.github.io/skyway/docs/
ということで、上記の「data」は dataConnection.sendで送っているだけです。
※ connection時のオプション指定で、serializationを「binary」にするのをお忘れなく。
おわりに
aタグにdownload属性なんてあったんだっていう発見ができました。そして、PeerJSの便利さも覚えてきたので、これらを使って何か作りたいと思ってます。
以上!
JavaScriptのメモリ消費について
今回は、メモ書き...テーマはJavaScriptのメモリ消費についてです。
JavaScriptのメモリモデルはガベージコレクションという技術を用いています。
この手法は、ガベージコレクター(以下、GC)が最適と判断したタイミングでメモリの開放を行います。
問題点としては、下記があげられます。
・タイミングが制御できない
・GCの処理中は、プログラムが利用可能な処理時間を奪う
要は、メモリ消費が大きいWebアプリはGCが頻発し、パフォーマンス劣化に繋がるよってこと。
GCの頻度はどうやって確認する?
下記の画像は、YahooのトップページをDeveloper ToolsのTimelineタブでレコーディングしたものです。山になっている水色部分がメモリの使用量で、一回ガクッと下がっています。
これがGCのタイミングで、ギザギザになってるほどGCの頻度が高いです。
(「バージョン 39.0.2159.0 canary (64-bit)」の画面です。)
図1. Developer ToolsのTimeline |
図2. Developer ToolsのTimeline |
GCの対象は?
JavaScript内ではすべての値がオブジェクトグラフに属しており、グラフにはルートが存在します。そのルートからの参照がなくなった時点で、値はガベージコレクションの対象になります。
(そして、GCの処理中はオブジェクトグラフがルートから順に走査される間はページのスクリプト実行は中断されるって感じになる。)
GCの頻度を下げるにはどうしたらいいか?
- メモリリークについて理解する
これについては説明が長くなりそうなので、下記の記事が非常に参考になります。
要は、不必要なクロージャに使用を避ける&循環参照を避けるってことがポイント。
【JavaScript】メモリの浪費を避けるコーディング
http://utage.headwaters.co.jp/blog/?p=1116
JavaScript アプリケーションのメモリー・リークを理解する
http://www.ibm.com/developerworks/jp/web/library/wa-jsmemory/
- 大量のオブジェクトを作成/破棄することをなるべくしないようにすればよい
スタティックメモリJavaScriptというテクニック
(があるらしい。こういう実装したことないので知識だけ書いておきます。)
1. 同じ型を持つオブジェクトを必要な分だけセットして保持しておく、オブジェクトプールと呼ばれるものを作る。
2. 新しいオブジェクトが必要となった場合、システムのヒープメモリから取ってくるのではなく、プール内の使用されていないオブジェクトを再利用する。
3. 不要になったオブジェクトは、メインメモリではなくプールに返却する。
つまり、最初にオブジェクトをゴソッと作って、必要な場合はゴソッと作った中のものを使う。
【メリット】
フレーム毎に生成/破棄されるオブジェクトの数を必要最低限に抑えることができる
【デメリット】
初期化時の挙動が重い
メモリをあまり使用していないときも、使用メモリ量は減らない
(その他..)
また、
'hoge'.slice() とか 'hoge'.substr() とか非破壊関数は新しく配列・文字列を作って返すので注意すべし。
終わりに
ここに書いたことは、担当している案件で動作が遅いとか、パフォーマンスチューニングして?って言われたときに気にするポイントの1つです。
ネットワーク部分以外のチューニングとしては、他に描画処理のパフォーマンスチューニングやJavaScriptのコード最適化(JSXみたいな)などがあります。
今後記事にできたらいいなーと思います。
以上!
参考
オブジェクトプールを使った静的メモリ JavaScripthttp://www.html5rocks.com/ja/tutorials/speed/static-mem-pools/
Gmail スケールの効率的メモリ管理術
http://www.html5rocks.com/ja/tutorials/memory/effectivemanagement/
Web Speech APIを使ってみる
Google ChromeでJavaScriptを使った音声認識ができるので、試してみました。Web Speech APIというのを使いますが、これは結構前から実装されています。
仕様はこちらに載っていますが、W3C標準ではありません。
https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
各ブラウザへの実装状況はこちらです。(赤はサポートしてない)
図. Web Speech API対応状況 |
http://caniuse.com/#feat=web-speech
サンプル
このAPIを使うと、音声解析とテキストの読み上げを実現することができます。サンプルは下記のコードです。(楽だったのでKnockout.js)
使い方(今年、1回でもアップデートしてるGoogle Chromeなら動くはず)
1. 「録音開始」ボタンを押す
2. マイクへのアクセス許可をします(ページ上部に確認が出る)
3. 「開始前」から「録音スタート」と文言が変化するので、適当に喋ってみる
4. 「録音開始」ボタン下部に喋った内容と「読み上げる」というボタンが表示される
5. 「読み上げる」ボタンを押すことで、喋った内容が読み上げられます
※ 間隔を空けながら喋れば、喋った内容の文字が繋がらず、下にどんどん追加されます。
ちなみに…
var recognition = new webkitSpeechRecognition();上記を実行した後のイベントは、下記で制御できます。
recognition.start();
onstart: 音声認識サービスのスタート時に発生。
onend: 音声認識サービスが切断したときに発生。onstart後に発生するイベント。
onaudiostart: オーディオをキャプチャするために開始したときに発生。
onaudioend: オーディオをキャプチャを終了したときに発生。onaudiostart後に発生するイベント。
onsoundstart: いくつかの音が検出されたときに発生。クライアント側の検出器を用いることによって、低レイテンシで生成される。(静かな環境で何も喋らないと、発生しないイベントでした)
onsoundend: 音が検出されないときに発生。onsoundstart後に発生するイベント。
onspeechstart: 音声認識のために使用される音声が開始されたときに発生。
onspeechend: 音声認識のために使用される音声が終了したときに発生。onspeechstart後に発生するイベント。
onerror: 音声認識サービスのエラーが発生したときに発生。SpeechRecognitionErrorのインターフェイスが返却される。
onresult: 音声認識サービスが結果を返すときに発生。SpeechRecognitionEventのインターフェイスが返却される。
onnomatch: 音声認識サービスは、信頼閾値を満たさないときに発生。
おわりに
これ、もっと精度が上がるか、精度を上げるような仕組みをちゃんと入れるか、テキスト解析APIに投げつけるかとか、色々応用することができそうです。WebRTCと一緒に使ってウンヌン…とかもできそう。
以上!
AngularJSとKnockout.js
筆者の担当していた案件では、Knockout.jsを使用していました。(詳しくはこっちの記事で)今回は双方向バインディングのお友達のAngularJSをいじりつつ、Knockout.jsだったらどうなる?的なことをチョコっと書いてみます。
どっちか知ってる人はきっと分かりやすいのかなーと思います。
AngularJSをいじる
AngularJSの基本的なディレクティブ↓を触ってみました。ng-model、ng-repeat、ng-init、ng-show、ng-hide、ng-if、ng-class、ng-click
コメント入れながら書いてみたので、大体意味は伝わるのかな?と思います。
他にもディレクティブはこんなにあります。
https://docs.angularjs.org/api/ng#directive
Knockout.jsをいじる
Knockout.jsで同じようなことをするとこうなります。
まとめ
両方共おんなじようなことはできるし、ディレクティブもバインディングも自身で追加することもできる。
便利。
そして、AngularJSはもっと色々な機能がついてる。
用途にって使い分けるとよさそう!
そして、AngularJSはもっと色々な機能がついてる。
用途にって使い分けるとよさそう!
以上!
CSSの設計について。
CSSは単純に書くだけであれば簡単ですが、運用していくと扱いが非常に難しいです。とくに、サーバーサイドあがりのフロントエンジニアにはCSS設計って何をどうしたらいいのさ?ってことになりがちです。
なので、今回はCSSの設計を考えさせられる題材にしました。
よりよいCSSとは?
Philip Walton氏のブログ記事の引用ですが、よりよいCSSは下記のような点があげられます。参考:http://urx.nu/bdMt
・よりよいCSS
予測しやすい
⇒ ルールが期待通りに振る舞うこと。
再利用しやすい
⇒ ルールが抽象的で、問題を書き直す必要がなく、既存のパーツから新しいコンポーネントを速く作れるということ。
保守しやすい
⇒ 新しいコンポーネントと機能が追加・更新されるとき、既存のCSSのリファクタリングを必要としない。
拡張しやすい
⇒ CSS設計の学習コストが低く、安易に管理できること。
・よくないCSS
親に基づいてコンポーネントを修正する
⇒ クラス定義を一旦終えてから、特定のユースケースのためだけにそのクラス定義を変更するようなこと。再利用性・保守性がない。
過剰に複雑なセレクタ
⇒ HTMLが今後まったく変わらないのであればメリットはありえるが、そんなケースはほぼない。再利用性・予測ができない。
過剰に一般的なクラス名
⇒ 「.name」「.title」などのクラスでは、何の名前なのか・タイトルなのか、予測しづらい。そして、一般的なクラス名はコンポーネント内で重複しやすい。
1つのルールで過剰にスタイルする
⇒ 1つのルールで全てを指定する場合、見た目は再利用可能だが、配置に関しては再利用しづらい。
言ってることはリーダブルコードなどに載っているようなことと大体一緒。
コンポーネント設計の概念的なもの
CSSにも設計について色々な概念があります。いくつか紹介していきます。
- OOCSS(Object Oriented CSS)
オブジェクト指向のスタイルシートのこと。
構造とスキンを分離してクラス定義して、それらを組み合わせてスタイルを定義するという方法。
詳細は下記の記事が分かりやすかったです。
難しいOOCSS(オブジェクト指向CSS)の設計: http://hijiriworld.com/web/oocss-design/
クラス数が増えるし、どこまで細かく分けて実装していくかなどに統制とれるようにしなきゃならないし、
セマンティックな人とも戦いが起こるかもしれない。
OOCSSとSass: http://takazudo.github.io/blog/entry/2012-12-10-oocsssass.html
大規模サイトとかになると、パフォーマンスに影響でてくるボリュームになるかも。
- BEM(ベム)
Block、Element、Modifierの略称で、CSSの構成をこの3つにカテゴライズする。
そして、そのカテゴリにそったクラス名をつける方法。
Block: 各パーツ(ヘッダー・フッター・商品説明・検索ボックスなど)のルートとなる要素。
⇒ 「Block名」のようなクラス名となる。
Element: Blockの構成要素で、クラス名には必ずBlock名を含める。
⇒ 「Block名__Element名」のようなクラス名となる。
Modifier: 基本のレイアウトは一緒だが、見た目や動きをつけたい場合に使う。そして、名前(name)と値(value)を使い、複数パターンの状態を作る。
⇒ 「Block名_name_value」のようなクラス名をつける。
こちらの記事が分かりやすかったです。
BEMによるフロントエンドの設計 基本概念とルール: https://app.codegrid.net/entry/bem-basic-1
クラス名はかなり長くなりそう。
- SMACSS(スマックス)
Scalable and Modular Architecture for CSSの略称。
基本的な概念はBEMと同じようなもので、CSSの構成を5つのカテゴリに分ける。
(ベース・レイアウト・モジュール・状態・テーマ)
ベース: サイト全体で要素そのもののデフォルトスタイルを定義する。また、CSSリセットもベースルールに含まれる。
⇒ IDやクラスは使わず、要素自体にスタイルをあてる。
レイアウト: ヘッダー・フッター・コンテンツエリア・サイドバーなどの構成の大枠を作る要素へのルール。IDを使って指定するのもあり。
ページのエリア分け。
⇒ ひと目で意味がわかるように、クラス名の接頭辞に「l-*」又は「layout-*」をつけることを推奨している。
モジュール: レイアウト以外の構成要素は大体モジュールとして捉える。(ボタン・見出しなど)
⇒ 命名規則は特にないが、親モジュール名を接頭辞につけたり、「m-*」や「module-*」などをつけてもよい。
状態: モジュールの状態を表すルール。is-状態というカタチでクラス名を指定する。
⇒ クラス名の接頭辞に「is-*」をつける。(is-toggle-activeとか
テーマ: テーマを切り替える機能が必要な場合のみ使うカテゴリ。body要素に対してtheme-sky-blueみたいなクラスを付与して切り替える、とか。
⇒ クラス名の接頭辞に「theme-*」をつける。
カテゴライズが多いが、案件内でキチッと決まってたらとても読みやすそうだし、作る際にも名前に悩まなくなりそう。
個人的にはBEMのような長いクラス名より好ましい。
こちらの記事に詳しく書かれていました。
SMACSSによるCSSの設計 ベースとレイアウト: https://app.codegrid.net/entry/smacss-1
SMACSS 読んだ: http://chroma.hatenablog.com/entry/2013/07/22/120818
今必要なCSSアーキテクチャ: http://www.slideshare.net/MayuKimura/css-25547100
- その他
MCSS(Multilayer CSS)
BEMとOOCSSの原理を基にした構成らしい。
Multilayer CSS: http://operatino.github.io/MCSS/ja/
FLOCSS(フロックス)
OOCSSやSMACSS、BEM、SuitCSSの原理を基にした構成らしい。
MCSSのレイヤー構成にも大きな影響を受けている。
FLOCSS: https://github.com/hiloki/flocss
他にも、色々ある。
まとめ
調べてて思ったのは、これらの設計思想を全部理解するのは辛いので、これらを元にしながら案件独自のルールに落としこむのが良さそう。
運用中などに破綻して、ベースから書き換えなきゃいけない(SASSを使ってる場合だと修正は一瞬で済んでもCSS差分は膨大)とかにならないよう、はじめにしっかりと決めたい部分です。
以上!
compassが遅い件 vol.3
「compassが遅い件 vol.2」の続きです。今回はcssの生成を並列化したいと思います。
ディレクトリ構成等々は、vol.1・vol.2と同様です。
またもやconfig.rbに処理を書き込むカタチになります。
※ vol.1に記載した↓とは別件です。
> 最終的には、このグループでのコンパイルをforkさせ、複数のグループを並列にコンパイルさせるとこまでやりました。
> 今回は、それをするための前準備になります。
必要なものをインストールする
今回は並列実行ということで、Parallelというgemを使いたいと思います。
(スレッド数の管理もしてくれるし、とても楽に導入できたので。)
・Parallelをインストール(筆者はMacです…)
$ sudo gem install parallel
・Compassのバージョン
$ compass -v
Compass 0.12.2 (Alnilam)
Copyright (c) 2008-2014 Chris Eppstein
Released under the MIT License.
Compass is charityware.
Please make a tax deductable donation for a worthy cause: http://umdf.org/compass
並列化が実感できる環境をつくる
「vol.2」で作成したsass/test.scssはコンパイルに10秒以上かかっていたので、test2.scssとtest3.scssをつくり、css生成がブロックされる状態にしておく。
$ cp -ip sass/print.scss sass/test2.scss
$ cp -ip sass/print.scss sass/test3.scss
準備オッケー。
通常のコンパイルをしてみる。
$ compass compile --time --force
identical stylesheets/ie.css (0.0s)
identical stylesheets/print.css (0.0s)
identical stylesheets/screen.css (0.03s)
identical stylesheets/test.css (18.165s)
identical stylesheets/test2.css (0.001s)
identical stylesheets/test3.css (0.0s)
Compilation took 18.201s
test.css生成に時間がかかり、test2.cssとtest3.cssの生成がブロックされているのが分かります。
ソースの修正
修正箇所は、sass_filesのループで回してるところをparallelを使って並列化させるだけ。
ここの処理を_config.rbで上書きし、並列化させる。
すると、上限2スレッドでcss生成が開始される。
(test.cssでブロック中にtest2.css、test3.cssが生成されてるのがわかる。
(test.cssでブロック中にtest2.css、test3.cssが生成されてるのがわかる。
$ compass compile --time --force
identical stylesheets/ie.css (0.001s)
identical stylesheets/print.css (0.001s)
identical stylesheets/screen.css (0.016s)
identical stylesheets/test2.css (0.001s)
identical stylesheets/test3.css (0.001s)
identical stylesheets/test.css (16.213s)
Compilation took 16.216s
オプションで指定できるようにする
例のごとく、環境変数でパラメータを受け取れるようにします。今回は、全ソース乗っけます。(いろいろ細かく書いていってたので、まとめの意味で。
下記のようなコマンドで実行できます。
$ ./bin/zoocompass compile --time --force --compile-threads 2
終わりに
だいぶ複雑になってきましたね。。
苦肉の策でもあるので、ほんとにどうしようもないときに。。とか使いどきを選んでいただきたい所存。
以上!
苦肉の策でもあるので、ほんとにどうしようもないときに。。とか使いどきを選んでいただきたい所存。
以上!
Knockout.jsで大規模開発してみた件について。
筆者の担当していた案件では、Knockout.jsを使って開発していました。今回はどんな感じで使ってたのか?というのを記事にしたいと思います。
Knockout.jsとは?
MVVM(Model-View-ViewModel)パターンをサポートするフレームワークです。
双方向バインディング云々…書こうとしましたが、こちらを参照したほうが良いでしょう。
わかりやすいです。
[Knockout]MVVMパターンでアプリケーションを構築する
どんな案件?
- 内容
webviewを使ったスマートフォン向けのアプリ開発。
- サポート端末
Android4.0以上
iOS6.0以上
- 案件規模
フロントエンジニアは5名程度。
- コード量
愚直に↓をしてみると、数十万行
$ find . -name "*.js" | xargs wc -l
いいとこわるいとこ
いいとこ
- 学習コストが低い
機能がシンプル(このくらい)というのもあるが、チュートリアルがしっかりしているので、これだけやればすぐ案件に入れるレベルになる。
チュートリアル:http://learn.knockoutjs.com/
- 機能はシンプルだが、拡張しやすい
data-bind内で指定する、バインディングを独自に拡張することができます。
例えば、
スマホの場合、clickイベントとtouchイベントを両方使うと問題が起きやすいので、
筆者の案件ではclickバインディングではtouchstartイベントを使うようにカスタムバインディングで上書きしている。
作り方は、こんなに簡単です↓
- ko.utilsというユーティリティがついてくる
地味に便利です。
ko.utils.unwrapObservableなどはカスタムバインディングを作る際によく使います。
- 分業できる
開発チーム内で、下記のように分業することができる。
フロントチームのコーディング担当
HTML+CSSを使ってViewのみを作成。作成後、JavaScript担当へパス。
独自タグもないので、とっつきやすい。
フロントチームのJavaScript担当
サーバーチームとAPIの設計について相談し、ViewModelを作成。
そして、コーディング担当から受け取ったViewにdata-bind属性を追加し動きをつける。
サーバーチーム
JavaScript担当と相談しAPIを作成する。
わるいとこ
- テストしづらい
Karma(mocha+sinon-chai)を使ってテストをするようにしましたが、Viewと紐付いているViewModelの実装はテストしづらかったです。
View側のテストもできないし、何かいい手はないかなーという感じでした。
(案件内ではテスト必須にはならず)
- だめというか、開発者が気をつけてなきゃいけないとこ
View側に複雑なロジックを入れないようにすること。
<!-- if hogehoge().length < 10 || nantokahuragu -->
このぐらいだとまだ良いが、ネストが深くなったり条件が複雑になると、可読性がものすごく下がる。
withの多様も要注意。
どんな感じで使ってたか
基本は、このような感じ。
(実際はファイル結合してminファイルを出力したり、styleはcssにちゃんと書いてたり、いろいろやってます。あと、ディレクトリ構成も適当です。)
javascriptでClass風の実装ができるようなライブラリを使い、継承して使用する。
※ 案件で使ってるコード・ライブラリではないです。
(バンバン継承しよう!というのは推奨せずでしたが。)
まあ、CoffeeScriptなりTypeScript使っとけよって感じかも知れませんが。
まあ、CoffeeScriptなりTypeScript使っとけよって感じかも知れませんが。
ある程度コードに規則性もできたので、保守しやすくなりました。
また、機能がシンプルというのもあり、
Knockout.jsのカスタムバインディングなどを使って、下記のような様々な仕組みも実装しました。
・1ページ内にViewModelが複数の場合の制御
・SEの制御
・ダイアログの制御
などなど
おわりに
フレームワーク選定の際に重視するのは、学習コストだったり運用のしやすさだと思うので、Knockout.jsは結構使いやすいんじゃないかなーと思いました。
また、使ってみてですが、筆者はKnockout.jsの不具合には遭遇しませんでした。
要素が多くなりすぎるとパフォーマンスに影響がでるということはありました。
(ko.applyBindings実行時に負荷がかかるが、Knockoutで処理させる箇所が数百箇所とかにならなければそんなに問題にならなかったです。)
小規模向け・プロトタイプで使うと良い。と書かれてる記事もよく見ますが、
小規模でも使いやすく、大規模でも使いやすいやつでした。
以上!
また、使ってみてですが、筆者はKnockout.jsの不具合には遭遇しませんでした。
要素が多くなりすぎるとパフォーマンスに影響がでるということはありました。
(ko.applyBindings実行時に負荷がかかるが、Knockoutで処理させる箇所が数百箇所とかにならなければそんなに問題にならなかったです。)
小規模向け・プロトタイプで使うと良い。と書かれてる記事もよく見ますが、
小規模でも使いやすく、大規模でも使いやすいやつでした。
以上!
WebRTCのRTCDataChannel APIを使って、複数人にデータを送信してみる
WebRTCで複数人接続してみるの続きです。今回は「RTCDataChannel API」を使って、データの送受信を試してみたいと思います。
複数人接続の記事の後なので、複数人に対してデータの送受信までを試してみます。
RTCDataChannel APIとは?
Peerの接続を利用して、データを送受信できる仕組み。・送受信できるデータ形式
Blob、ArrayBuffer、ArrayBufferViewのようなデータ形式をサポート。
・通信プロトコル
UDP、TCP、SCTPをサポート。
RTCDataChannel APIを使うには?
こんな感じで生成することができます。DataChannelの接続リクエストをハンドリングする。
これで送信する準備はオッケーです。
下記をconsoleから実行すると端末B側の「dataChannel.onmessage」が動作します。
remoteDataChannel.send("test test");
注意点ですが、Peerでの接続確立後に「peer.createDataChannel」をしても相手側の「peer.ondatachannel」は動作しませんでした。
オファー前に「peer.createDataChannel」をしとくと良いようです。
参考:http://blog.wnotes.net/blog/article/beginning-webrtc-datachannel
前回のソース(WebRTCで複数人接続してみる)を修正してみる
webrtc.jsを修正する・initの処理を変更(this.dataChannelsとthis.remoteDataChannelsを初期化させる処理追加
・connectの処理を変更(this.option.dataChannelsのif文を追加
・createDataChannelを新規追加
index.htmlを修正する
・createWebrtcInstanceの処理を変更(new するところに、dataChannelsを追加しただけ
追加してるコードは50行ぐらいです。
データ送受信の実行手順
1. WebRTCで複数人接続してみるの「作ってみた」の手順通り実施し、複数人接続をさせておく2. 全てのブラウザでDeveloper Toolsを開く
3. どれか1つでconsoleから下記を実行する
for (var i in webrtcTest.webRtcInstances) {4. 実行したタブ以外で、下記のようなメッセージがconsole上に流れていることを確認できる
if (i !== webrtcTest.localInstanceKey) {
webrtcTest.webRtcInstances[i].remoteDataChannels['test channel'].send("test dayo");
}
}
data channel message:test dayoできた!!
(個別に送信したい場合は、下記を実行する
webrtcTest.webRtcInstances[id].remoteDataChannels['test channel'].send("test dayo");※ idは送信したい端末のid
終わりに
ここまでやったらWebRTCはお腹いっぱい気味。つぎは、ファイルの送受信をできるようにするかなあ。。
(チャンクさせるようにするなんて面倒だ。。
まあ、そこまでやるならサービスレベルのものを作るときかなー。
あとは、peer.jsとかライブラリを使ってみるとか。
気が向いたらやります。
参考
・WebRTC data channelshttp://www.html5rocks.com/ja/tutorials/webrtc/datachannels/
・WebRTC-DataChannel使ってみたよ
http://blog.wnotes.net/blog/article/beginning-webrtc-datachannel
以上!
WebRTCで複数人接続してみる
WebRTCをさわってみる&手動シグナリングしてみるの続きです。今回は、WebRTCを使って複数人接続してみたいと思います。
1対1で接続するときとの違いは?
前回のおさらいですが、1対1で接続するとこんな感じになる。
図1. 1on1の接続 |
No | 端末A | 端末B |
1 | video要素を作っておく | video要素を作っておく |
2 | Peerを生成 | Peerを生成 |
3 | メディアに接続(カメラの起動を許可すること) ・Peerにストリームを接続し、自身の端末の状態をvideo要素で閲覧できる状態になる | メディアに接続(カメラの起動を許可すること) ・Peerにストリームを接続し、自身の端末の状態をvideo要素で閲覧できる状態になる |
4 | 端末Bへ送信するOfferを生成 | |
5 | 端末Aで生成したOfferを受信 | |
6 | 端末Aへ送信するAnswerを生成 | |
7 | 端末Bで生成したAnswerを受信 | |
8 | 端末Bへ送信する経路情報を出力 | |
9 | 端末Aで生成した経路情報を受信 | |
10 | 端末Aへ送信する経路情報を出力 | |
11 | 端末Bで生成した経路情報を受信 |
じゃあ、複数人接続の場合はどうなるのか?
3端末のときの接続を例として記述します。
上記のように接続するには、
端末Aと端末Bと端末Cがそれぞれ、下記のようなPeerを生成しなければなりません。
・端末Aと端末BをつなぐPeerの生成、端末Aと端末CをつなぐPeerの生成
・端末Bと端末AをつなぐPeerの生成、端末Bと端末CをつなぐPeerの生成
・端末Cと端末AをつなぐPeerの生成、端末Cと端末BをつなぐPeerの生成
そして、4台目が追加になる場合は更に1つずつPeerを生成することになり、
それを動的に通知して接続処理をするようにしなければなりません。
結構複雑っす。
どういう流れで複数接続させるか?
3端末での接続手順を記載します。(灰色のセルは端末未接続時・25〜38、25`〜38`は並行で処理されます)
No | 端末A | 端末B | 端末C |
1 | 特定URLにアクセス | ||
2 | socket.ioでコネクションをはる | ||
3 | コネクションをはってるユーザー全員にコールする | ||
4 | いなかったので終わり。 | ||
5 | 特定URLにアクセス | ||
6 | socket.ioでコネクションをはる | ||
7 | コネクションをはってるユーザー全員にコールする | ||
8 | 端末Bからコールされた | ||
9 | 端末Aのsocketのidを端末Bに返却する | ||
10 | 端末Aのsocketのidを返却される | ||
11 | Peerを生成 | ||
12 | socketのidを元に、端末AへSDPを送信(オファー) | ||
13 | Peerを生成 | ||
14 | 端末BのSDPを受信(オファーをレシーブ) | ||
15 | socketのidを元に、端末BへSDPを送信(アンサー) | ||
16 | 端末AのSDPを受信(アンサーをレシーブ) | ||
17 | 端末Aへ端末Bまでの経路情報を送信 | ||
18 | 端末Bから経路情報を受信 | ||
19 | 端末Bへ端末Aまでの経路情報を送信 | ||
20 | 端末Aから経路情報を受信 | ||
21 | Peer to Peer! | Peer to Peer! | |
22 | 特定URLにアクセス | ||
23 | socket.ioでコネクションをはる | ||
24 | コネクションをはってるユーザー全員にコールする | ||
下記のAとC / BとCのやりとりは並行して行われる | |||
25 | 端末Cからコールされた | ||
26 | 端末Aのsocketのidを端末Cに返却する | ||
27 | 端末Aのsocketのidを返却される | ||
28 | Peerを生成 | ||
29 | socketのidを元に、端末AへSDPを送信(オファー) | ||
30 | Peerを生成 | ||
31 | 端末CのSDPを受信(オファーをレシーブ) | ||
32 | socketのidを元に、端末CへSDPを送信(アンサー) | ||
33 | 端末AのSDPを受信(アンサーをレシーブ) | ||
34 | 端末Aへ端末Cまでの経路情報を送信 | ||
35 | 端末Cから経路情報を受信 | ||
36 | 端末Cへ端末Aまでの経路情報を送信 | ||
37 | 端末Aから経路情報を受信 | ||
38 | Peer to Peer! | Peer to Peer! | |
25` | 端末Cからコールされた | ||
26` | 端末Bのsocketのidを端末Cに返却する | ||
27` | 端末Bのsocketのidを返却される | ||
28` | Peerを生成 | ||
29` | socketのidを元に、端末BへSDPを送信(オファー) | ||
30` | Peerを生成 | ||
31` | 端末CのSDPを受信(オファーをレシーブ) | ||
32` | socketのidを元に、端末CへSDPを送信(アンサー) | ||
33` | 端末CのSDPを受信(アンサーをレシーブ) | ||
34` | 端末Bへ端末Cまでの経路情報を送信 | ||
35` | 端末Cから経路情報を受信 | ||
36` | 端末Cへ端末Bまでの経路情報を送信 | ||
37` | 端末Bから経路情報を受信 | ||
38` | Peer to Peer! | Peer to Peer! |
基本的な流れは1対1の時と一緒ですが、
Peerの生成タイミングや、Peer生成後にローカルのストリームとの接続手順などが変わってきます。
(「コネクションはっているユーザー全員にコールする」のレスポンスでオファー送ることもできた。そうすると、通信回数としては最小になりそう。)
作ってみた
このソースを使うと、↓↓のような画面がでるので、手順にそって作業してみてください。・シグナリングサーバー(Node.js)
これをして、node_modulesを取得してください。
$ npm installそして、これで起動する。
$ node app.js
・Webサーバー(Class風実装は特に見ないでいいです。。
(長いっすね。。
上記のソースをDocumentRoot以下に配置し、ブラウザからアクセスして、動作確認をしてみる。
1. 特定のURLへアクセス(筆者はここhttp://localhost/index.html
図3. カメラ・マイクへのアクセス確認 |
2. カメラの表示確認
図4. カメラ動画表示 |
そして、「ここを押したら繋がるけどカメラを先に許可しといてね」ボタンを押すことで、シグナリングサーバーへアクセスします。
(自分がページを開いてるよ。ってことを通知してるだけで、ここの手順では何も起こりません。)
※上記手順の1〜4です。
3. リモートのカメラ画像を表示する
図5. リモートのカメラ動画表示 |
終わりに。
タブを複数開いて通知確認しながらテストしてたのですが、頭がこんがらがるし、結構たいへんでした。シグナリングサーバーを応用! 「WebRTCを使って複数人で話してみよう」を参考にさせて頂きました!
つぎこそはDataChannelやるぞー!
以上!
Web ComponentsとPolymerをさわってみる。
いまさらですが、Web Componentsってナニ?
ってことをサラッと書きたいと思います。
下記のW3C 提案をまとめてWeb Components というらしい。
Custom Elements
⇒ ウェブ開発者が HTML に独自のタグを追加できる仕組み。
HTML Imports
⇒ HTML Imports は別ファイルに定義した Custom Elements をページ内に読み込む仕組み。
(プログラミング言語の require や import みたいなもの)
HTML Templates
⇒ ページ本体のDOMと切り離されたHTMLを生成することができる仕組み。
(HTML Templates に定義された <template> タグでテンプレート用の HTML を囲んでおくと、 ページ本体の DOM とは切り離され、<img> や <video> を書いてもロードはおこらないし、<style> は適用されず <script> は実行されない)
Shadow DOM
⇒ DOM要素のレンダリング結果を、その要素が持つDOM のサブツリーとは独立に与える仕組み。
(DOMツリーをカプセル化し、ページの他の部分からDOMツリーを分離することができる)
Web Animations
⇒ CSS Animations と CSS Transitions と SVG Animations を 統合する仕組み。
Pointer Events
⇒ マウス、タッチ、ペンのイベントを統一する仕組み。
そこで、Polymerの提供しているplatform.jsを使えば該当APIをJavaScriptで再実装し、ブラウザにまだない機能を補ってくれる。
Web ComponentsのPolyfillライブラリがplatform.jsで、その上で動作するUIフレームワークがpolymer.js。
Polymerにはdesigner toolもあり、コードを書かなくてもUIを生成することができます。
(使ったことないけど、いい感じなのかな?)
更に、Polymerにはレイアウトの例だったり、アイコン群のサンプルがあります。
PolymerとWeb Componentsの関連は、下記を見ると分かりやすいです。
(各APIの土台の上に、platform.jsがあり、polymer.jsがある)
*画像をお借りしました!
(/test/webcomponents/index.htmlにおく
ブラウザで下記URLにアクセスすると、こんな表示になるとはず。(線と右上のは気にしないでください…
http://localhost/test/webcomponents/index.html
ちなみに、polymer使用時のネットワークタブはこんな感じになります。
ソース見てて思ったんですが、結構直列で取得しにいってますね。
この順番で取得しにいってます。
1. test/webcomponents/index.html
2. bower_components/core-toolbar/core-toolbar.html
3a. bower_components/core-toolbar/core-toolbar.css
3b. bower_components/polymer/polymer.html
4a. bower_components/polymer/layout.html
4b. bower_components/polymer/polymer.js
この仕組み、結構遅いんじゃないの…
ということで、インラインにしてくれる仕組みなどが既にあるようです。
まあ、これだけじゃよく分からないと思うので、次回以降に詳細を書ければいいなーと思います。
Polymerウンヌンから入るより、Web Componentsの各APIの詳細を掘り下げていったほうが分かりやすい気がする。
ってことをサラッと書きたいと思います。
Web Componentsとは
Web Componentsは新しく提案されたウェブブラウザ向け API 一式の総称。下記のW3C 提案をまとめてWeb Components というらしい。
Custom Elements
⇒ ウェブ開発者が HTML に独自のタグを追加できる仕組み。
HTML Imports
⇒ HTML Imports は別ファイルに定義した Custom Elements をページ内に読み込む仕組み。
(プログラミング言語の require や import みたいなもの)
HTML Templates
⇒ ページ本体のDOMと切り離されたHTMLを生成することができる仕組み。
(HTML Templates に定義された <template> タグでテンプレート用の HTML を囲んでおくと、 ページ本体の DOM とは切り離され、<img> や <video> を書いてもロードはおこらないし、<style> は適用されず <script> は実行されない)
Shadow DOM
⇒ DOM要素のレンダリング結果を、その要素が持つDOM のサブツリーとは独立に与える仕組み。
(DOMツリーをカプセル化し、ページの他の部分からDOMツリーを分離することができる)
Web Animations
⇒ CSS Animations と CSS Transitions と SVG Animations を 統合する仕組み。
Pointer Events
⇒ マウス、タッチ、ペンのイベントを統一する仕組み。
Polymerとは?
新しいAPI に依存してしまい、一体どのブラウザなら動くのかということになる。そこで、Polymerの提供しているplatform.jsを使えば該当APIをJavaScriptで再実装し、ブラウザにまだない機能を補ってくれる。
Web ComponentsのPolyfillライブラリがplatform.jsで、その上で動作するUIフレームワークがpolymer.js。
Polymerにはdesigner toolもあり、コードを書かなくてもUIを生成することができます。
(使ったことないけど、いい感じなのかな?)
更に、Polymerにはレイアウトの例だったり、アイコン群のサンプルがあります。
PolymerとWeb Componentsの関連は、下記を見ると分かりやすいです。
(各APIの土台の上に、platform.jsがあり、polymer.jsがある)
図. PolymerとWeb Components |
Polymerを試してみる
・インストールする
(筆者はDocumentRoot以下の、/test/webcomponentsで実行
(筆者はDocumentRoot以下の、/test/webcomponentsで実行
# Bower のインストール・実行用のソースを用意する
$ sudo npm install bower -g
# 初期化処理
$ bower init
# Polymer のインストール
$ bower install --save Polymer/polymer
# コンポーネントのインストール
$ bower install --save Polymer/core-elements
$ bower install --save Polymer/paper-elements
(/test/webcomponents/index.htmlにおく
<!DOCTYPE HTML>・アクセスする
<html lang="ja">
<head>
<meta charset="UTF-8">
<title></title>
<script src="bower_components/platform/platform.js"></script>
<link rel="import" href="bower_components/core-toolbar/core-toolbar.html">
</head>
<body>
<core-toolbar>TEST</core-toolbar>
</body>
</html>
ブラウザで下記URLにアクセスすると、こんな表示になるとはず。(線と右上のは気にしないでください…
http://localhost/test/webcomponents/index.html
図. core-toolbarをつかう |
ちなみに、polymer使用時のネットワークタブはこんな感じになります。
図. polymer使用時のネットワークタブ |
この順番で取得しにいってます。
1. test/webcomponents/index.html
2. bower_components/core-toolbar/core-toolbar.html
3a. bower_components/core-toolbar/core-toolbar.css
3b. bower_components/polymer/polymer.html
4a. bower_components/polymer/layout.html
4b. bower_components/polymer/polymer.js
この仕組み、結構遅いんじゃないの…
ということで、インラインにしてくれる仕組みなどが既にあるようです。
まあ、これだけじゃよく分からないと思うので、次回以降に詳細を書ければいいなーと思います。
Polymerウンヌンから入るより、Web Componentsの各APIの詳細を掘り下げていったほうが分かりやすい気がする。
終わりに
この技術が標準化されてきたら、Web業界の職種も変わってきそうですね。
web componentsストアみたいなのできないかなー
参考にさせて頂きました!
Web Component概要
web componentsストアみたいなのできないかなー
参考にさせて頂きました!
Web Component概要
Polymer と Web Components
以上!
WebRTCをさわってみる&手動シグナリングしてみる
今回は、WebRTCについて書きたいと思います。WebRTCって何ぞ?
WebRTCって何ぞ?ってとこから書きたいなと思ったのですが、他の方の記事見てるとよく書けてるな。。
改めて書く必要ないんじゃないかな。。
図ないと分かりづらいよな。。面倒だなこれ。。
って感じになりました。
ということで、WebRTCって何ぞ?とかは下記の記事を参考にしてください!
・WebRTCを仕組みから実装までやってみる
http://blog.wnotes.net/blog/article/webrtc-beginning
・WebRTCで変わるWebの未来
http://www.qcontokyo.com/data_2013/ToruYoshikawa_QConTokyo2013.pdf
んで、今回書くのは
P2Pで通信を開始するまでのシーケンスを1手順ずつ、自分でわかるように、自分のために書きます。
今回やろうとすること
端末A・端末BをPeer to Peerし、Macに内蔵しているカメラで撮影している内容をリアルタイムに共有するところまでやる。
必要っぽいもの
シグナリングサーバーICEサーバー(STUN / TURN)
と思ったけど、シグナリングは手動でできそうだし、ICEサーバーだけでいいんじゃないか感。
ということで、今回は↓でやります。
・シグナリングは手動
・Googleが提供しているSTUNサーバー
接続手順
事前準備(共通)No | 端末A | 端末B |
1 | video要素を作っておく | video要素を作っておく |
2 | Peerを生成 | Peerを生成 |
3 | メディアに接続(カメラの起動を許可すること) ・Peerにストリームを接続し、自身の端末の状態をvideo要素で閲覧できる状態になる | メディアに接続(カメラの起動を許可すること) ・Peerにストリームを接続し、自身の端末の状態をvideo要素で閲覧できる状態になる |
この作業は接続する端末共通です。
ここまで実施することで、ブラウザで自身の姿が見れるようになるはずです。
接続対象へSDPを送受信してセッションを確立
Simple Traversal of UDP through NATs (STUN): NAT越えの方法としてRFC3489で定められた標準的な仕組み。
外部のSTUNサーバに対してクライアントが一度接続し、グローバルIPとマッピングされたポート番号を記憶しておくことで、そのデータを使ってPeerは相手のマシンを特定することができる。
No | 端末A | 端末B |
4 | 端末Bへ送信するOfferを生成 | |
5 | 端末Aで生成したOfferを受信 | |
6 | 端末Bへ送信するAnswerを生成 | |
7 | 端末Bで生成したAnswerを受信 |
リモート側のストリームを共有する
接続経由を共有することで、端末Aと端末BをPeer to Peer接続する。
No | 端末A | 端末B |
8 | 端末Bへ送信する経路情報を出力 | |
9 | 端末Aで生成した経路情報を受信 | |
10 | 端末Aへ送信する経路情報を出力 | |
11 | 端末Bで生成した経路情報を受信 |
ここまでくると、接続した端末のカメラの動画が表示されるようになっているはずです。
まとめて書くと、こんな手順です。
No | 端末A | 端末B |
1 | video要素を作っておく | video要素を作っておく |
2 | Peerを生成 | Peerを生成 |
3 | メディアに接続(カメラの起動を許可すること) ・Peerにストリームを接続し、自身の端末の状態をvideo要素で閲覧できる状態になる | メディアに接続(カメラの起動を許可すること) ・Peerにストリームを接続し、自身の端末の状態をvideo要素で閲覧できる状態になる |
4 | 端末Bへ送信するOfferを生成 | |
5 | 端末Aで生成したOfferを受信 | |
6 | 端末Aへ送信するAnswerを生成 | |
7 | 端末Bで生成したAnswerを受信 | |
8 | 端末Bへ送信する経路情報を出力 | |
9 | 端末Aで生成した経路情報を受信 | |
10 | 端末Aへ送信する経路情報を出力 | |
11 | 端末Bで生成した経路情報を受信 |
手動でやると面倒ですね。これ。
接続してみよう!
コードはこんな感じ(1〜11の上記手順通り番号をつけています。このコードをブラウザで表示すると、こうなります。
図. webrtcテストサンプル |
(カメラを許可すると、左上に端末A(自分の顔)が表示されます。
では、ブラウザを2つ立ち上げ(別のPCでも可)、端末Aと端末Bとして作業してみましょう。
4〜11は下記のような作業が必要なので実行してみてください。
図. 手順説明用 |
No | 端末A | 端末B |
4 | 端末Bへ送信するOfferを生成 ・Developer ToolsのConsoleで下記を実行し、表示される文字列をコピーする(文字列に変換して表示されてるやつ webrtc.createOffer() | |
5 | 端末Aで生成したOfferを受信 ・4でコピーした文字列をブラウザに表示されてるテキストエリア左側(図の①)に貼り付け、Consoleから下記を実行する webrtc.receiveSdp() | |
6 | 端末Aへ送信するAnswerを生成 ・Consoleで下記を実行し、表示される文字列をコピーする(文字列に変換して表示されてるやつ webrtc.createAnswer() | |
7 | 端末Bで生成したAnswerを受信 ・6でコピーした文字列をブラウザに表示されてるテキストエリア左側(図の①)に貼り付け、Consoleから下記を実行する webrtc.receiveSdp() | |
8 | 端末Bへ送信する経路情報を出力 ・Consoleで下記を実行し、表示される文字列をコピーする(文字列に変換して表示されてるやつ webrtc.displayCandidates() | |
9 | 端末Aで生成した経路情報を受信 ・8でコピーした文字列をブラウザに表示されてるテキストエリア右側(図の②)に貼り付け、Consoleから下記を実行する webrtc.receiveCandidates() | |
10 | 端末Aへ送信する経路情報を出力 ・Consoleで下記を実行し、表示される文字列をコピーする(文字列に変換して表示されてるやつ webrtc.displayCandidates() | |
11 | 端末Bで生成した経路情報を受信 ・10でコピーした文字列をブラウザに表示されてるテキストエリア右側(図の②)に貼り付け、Consoleから下記を実行する webrtc.receiveCandidates() |
11を実行することで、右上に端末B(相手の顔)が表示されます。
おわりに
手動でやるとことで、流れがだいぶ理解できました。
今度は自動でシグナリングするようにして、DataChannelでもいじってみたいと思います。
以上!
登録:
投稿
(
Atom
)
0 件のコメント :
コメントを投稿