自習室

こもります

(タブレットで) 鏡餅を光らせて新年を迎えましょう | 実装編

はじめに

前回は、おうちハック Advent Calendar 2014 向けとして記事を書きました。そちらでは全体の紹介と、取り組んだ背景みたいなところにフォーカスして書いたので、今回は自分向けに実装で手こずったところ等をメモしていきます。自分用のメモなので雑多でごめんなさい。

目次

  • socket.io でルーム管理。おもち側が閉じたら、繋いでいるコントローラに伝達する仕組みなど
  • 様々なブラウザ・解像度への対応 (軽めに…
  • さくらのVPSに Node.js アプリをデプロイする際のTips
  • そのほか細かい技

おさらいのコード

記事中にコードを展開していくとバカ長くなりそうなので、言及場所にリンクを貼るスタイル。

おさらいの動画

おさらいのシステム図

f:id:AMANE:20141225104949p:plain

socket.io でルーム管理。おもち側が閉じたら、繋いでいるコントローラに伝達する仕組みなど

前回の記事でもちらっと書きましたが、今回の案件、本来なら(おそらく) WebRTC を使ってやるべき案件です。光る端末に対し、リモコンが直接指示を出します。基本は1対1で、始まった後は間にサーバが挟まっている必要はありません。

しかし今回は、自分の不勉強のため短いスパンで WebRTC を使いこなせそうになかったので、使ったことのあるsocket.io を使い、間にサーバが挟まるシステムにしました。そのため、利用者が増えると明らかに不利です。アドベントカレンダー向けのネタアプリですので勘弁してください。

そんな中でも一応こだわって頑張った辺りについてメモしておきます。

room名によるおもちの重複防止

おもちとコントローラの対応付けは、socket.ioが備えているroomの仕組みを使って行っています。roomは一般的なチャットルームを実装するための基本機能なのですが、

  • 存在しないルーム名でjoinしたら、新たにルームが作られる
  • 既存のルーム名でjoinしたら、既存のルームに入る

という動作になっております。

OMOCHI HACK ではこの仕組みを利用しつつ「おもちは必ずオリジナルな名前のルームとして作って、そこにコントローラが名指しでjoinする」仕組みとし、おもちの名前は重複を許さないことにしました。

  • おもち側で存在しないルーム名で入ろうとしたら、joinする → 新たにルームが作られる
  • おもち側で既存のルーム名で入ろうとしたら、既に使われている名前であると警告を出し、再入力を促す

この辺りの処理は、 server.js L62 - L112 あたり にコーディングしてあります。socket.ioのルーム管理とは別に、独自に現存するおもちのリストを管理することで実現しています。

おもち死亡のおしらせをコントローラ側に通知する

通常利用だと、おもち側が閉じるケースとして以下の様な物があります

  • ページをリロードする
  • ブラウザの戻るボタンを利用するなどページ遷移を行う
  • タブを閉じる
  • スマホを放置してしまい画面がロックされる
  • スマホで別のアプリを開く
  • 別の名前のおもちを作り直す

これらを検出したうえで、以下の事をやらなければなりません

  • ルーム(おもち)をリストから削除する(利用された名前がたまってしまわないよう)
  • おもちが消えたことをコントローラに通知する
window.onpagehide を利用する
  • ページをリロードする
  • ブラウザの戻るボタンを利用するなどページ遷移を行う
  • タブを閉じる

この3パタンについては、window.onpagehide で検出できます。はじめ window.onbeforeunload で書いていたのですが、Safari@iOS8 ではこのイベントが使われなくなって居ることを知り、pagehide に書き換えています。

この辺りの処理は、light.js L133 あたり にコーディングしてあります

socket.io の disconnect イベントを検出する
  • スマホを放置してしまい画面がロックされる
  • スマホで別のアプリを開く

この2パタンの場合、私が調べた限りでは ウェブアプリがそれを検出して何かする、というのが出来ませんでした。しかし、socket.io の方で、クライアント(ここではおもち)が不活性な状態を検出してdisconnect イベントとして処理する仕組みがあるようで、それを利用する事にしました。

具体的には、disconnectイベントを送ってきたsocketのidを調べて、それがおもちリスト(コード内では lightlist )に存在するか調べ、していたら削除する、と言う物です

この辺りの処理は server.js L49 - L55 あたりにコーディングしています

イベントを拾えるケース
  • 別の名前のおもちを作り直す

最後のパタンは、サーバ側で処理しています。自分が既におもちとしてroom名を持っていて、それと同じ名前で作ろうとした場合は、何もせず既存のroomを利用します。新しい名前を指定した場合は、さっきまで使っていたroomを破棄し、新しいroomを作ります。

この辺りの処理は、 server.js L105 - L109 あたりでコーディングしています。

おもちが消えたことをコントローラに通知する

上記パタンでおもちが消えたとき、コントローラ側は、カラーコントロールのUIがdisableされ、再度おもちの名前入力を要求される画面になります。これで、接続が切れたことをコントローラ側からも認識出来ます。この辺りの処理は sever.js L138controller.js L123 にコーディングしてあります。

f:id:AMANE:20141226202130p:plain

様々なブラウザ・解像度への対応 (軽めに…

ちまたのウェブデザイナ、ウェブコーダ様方にあたられましては、日々多様化するディスプレイの解像度や、ブラウザごとの微妙に異なるCSSの解釈に日々頭を悩ませていることと存じ上げますが、私も今回のアプリを作り上げるに当たりまして、「出来る範囲で・時間が許す範囲で」広めに対応を取ってみたところです

動作確認しているのは以下の環境

です。すべて最新の物です。

レスポンシブ的CSS切り分け

  • @media screen and (min-width: 320px) and (max-width: 480px) 的な物を使う

vendor prefix

-webkit-transform -moz-transform とか。

ブラウザごとの対応状況を調べられる。おなじ webkit でも、Chromeではつかえて、 safari@iOS じゃ使えない、とかかなりよくある。

Chrome for Androidborder-radius が微妙に効かない件

コントローラの円形のカラーピッカーを描くのに <div> に対して四隅50% でradius をかけようとしたが、Androidでだけうまくいかない。

f:id:AMANE:20141225220038j:plain

ここを参考に修正

.selector {
/* だめ */
border-radius: 50%;

/* だめ */
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;

/* OK */
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-left-radius: 50%;
border-bottom-right-radius: 49.9%; /* ここがポイント */

/* これでもOK */
border-radius: 50% 50% 50% 49.9%;
}

この記事では Galaxy S4 で、 と言っている。私の場合は Nexus7 (2013)。機種依存かもしれない。

さくらのVPSに Node.js アプリをデプロイする際のTips

基本

forever さまさまです。

この記事では /etc/nginx/conf.d/ 内にリバースプロキシの設定を書いていますが、

  1. /etc/nginx/sites-available/ 内に今回のアプリ用の新しい設定ファイルを作っておき
  2. /etc/nginx/sites-enabled/シンボリックリンク しておくのがいまどき、なはず

linux - What is the different usages for sites-available vs the conf.d directory for nginx - Server Fault

自分が作ったサイトの設定ファイルはこんな感じ

# /etc/nginx/sites-available/omochi_hack

upstream omochi {
    server localhost:3000;
}

server {
    listen 80; 
    server_name omochi.izmiz.me;

    proxy_redirect                          off;
    proxy_set_header Host                   $host;
    proxy_set_header X-Real-IP              $remote_addr;
    proxy_set_header X-Forwarded-Host       $host;
    proxy_set_header X-Forwarded-Server     $host;
    proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;

    location / {
        proxy_pass http://omochi/;
    }
}

VPSのポート設定

そういえば以前iptablesで厳しくポートを絞っていたのを忘れていました。express が ポート3000番でサーバをあげているので、あけてあげなくちゃいけません。

sudo ufw allow 3000/tcp

おなまえ.com のDNS設定

リバースプロキシに設定した omochi.izmiz.me がちゃんと解決出来るよう、DNSに覚えていただきます

おなまえ.com > ログイン > 今回のドメイン > (タブ)ドメイン設定 > DNS関連機能設定 > 今回のドメインを選んで「次へ進む」 > DNSレコード設定

ログインした状態で直リンク > https://www.onamae.com/domain/navi/dns_controll/input

普通にたどるととんでもない階層にいます。ほとんどここしか使わないのに…!

ここに、 nginx の sites-availabe に設定した server_name を登録します。

以上

以上で、 http://omochi.izmiz.me にアクセスすると、 さくらVPS内の expressが立てている 3000 番ポートで動いている OMOCHI HACK アプリにアクセス出来るようになりました。

そのほか細かい技

今回のアプリで参考にさせていただいたその他のノウハウをまとめておきます

Canvas + image でカラーピッカー

letter-spacing したテキストをセンタリングする

index.html L25 - L32

letter-spacing: 1em すると、文末にも1文字スペースが空いてしまうので、その分文末に負のマージンを与えるか、文の頭に正のマージンを与える

ブラウザの「ホーム画面に追加」を利用する

これをしてホーム画面からアイコンタップで起動すると、全画面でアプリが開きます。ここで登録されるアイコンについての覚え書き

log4js でログ取り

ロギングのセッティングを json で外部化出来るのが素敵。

内で1行のテキストを垂直方向にセンタリング

.selector {
  height: 100px;
  line-height: 100px; // heightline-height に同じ値を指定する
}

JavaScriptHSV -> RGB 変換

jQuery.delay() が便利

jQuery .val() で form の値を取得

jQueryセレクターを取得

$something.selector で取得 出来るのだけど、 deprecated. どうしましょう。今回使ってしまいました。

カラーホイールの絵の描き方

さいごに

今回、Advent Calendar締切ドリブンで、UI、通信、サーバサイド、サーバへのアプリのデプロイ と1人で通貫でやってみました。できあがった物はまぁしょぼいのですが、個人的には非常に大変でした。良い経験になりました。