「Canvas」と一致するもの

最近、Angular以外も試そうと思って、2012年春にオープンソースになったときから気になっていたMeteorを使うことにしました。 当時は、FirefoxかChromeかどちらかでしか動かず、動かない方で見ると、背景画像しか見えないというすさまじいものでした。 一方、そのコンセプトはとても共感できて、social-cms-backendを自分で開発したときも同じではないものの似た問題意識でした。

さて、Meteorのサイトは、

http://meteor.com/

です。いいドメイン持ってますね。Meteorの発音は「ミーティア」です。 T.M.Revolutionの歌にありましたね。よし、それを聞きながらコーディングしよう。

さて、Meteorの基本的な説明は他に任せるとして、今回はちょっと変わった使い方をしようと思います。そもそも、普通のWebアプリを作るなら慣れたAngularの方が早いのですが、canvasベースのアプリを作りたいと思ったのがきっかけでした。canvasベースのアプリだとAngularの恩恵があまり得られないので、Angular以外を試してみようと。細かい経緯は省きますが、下記のサイトを見てやってみようと思いました。

http://www.html5gamedevs.com/topic/4886-how-to-use-phaser-with-meteor/

Phaserというのはhttp://phaser.io/で、WebGL/Canvasでゲームを作るフレームワークです。Phaserの説明も他に任せます。上記の記事はちょっと古くて、今では、

https://atmospherejs.com/robertlowe/meteor-phaser

が使えます。MeteorのパッケージがAtmosphereというのは、かっこいいですが、慣れるまでは戸惑いそうですね。

さて、お題は1bitコミュニケーションツールを考えています。 夏の暑いときに作った「暑さをみんなでふっとばせ!」というアプリを移植してみようと思います。このアプリについては、下記を参照。

1bitで使い捨てで匿名のコミュニケーションツールを作ってみた

Famo.us/AngularのサンプルアプリをGitHubに置きました

これは、Famo.us/Angularで作りました。とてもお手軽だったのですが、ちょっとゲームのようなものに使うにはパフォーマンスに難がありました。そこで今回は同じものをMeteor/Phaserで作ってみようと思います。

この記事を書きながらコーディングするのでうまく行くか分かりません。また、読みにくくなってしまうかもしれませんがご了承ください。

さて、まずはMeteorをインストールします。https://www.meteor.com/installに従います。このチュートリアルはよくできてますね。

$ curl https://install.meteor.com/ | sh

1.0.1になってroot権限も要求しなくなりました。1.0のときも、sudoのパスワードを入れなければ同じだったのですが、これはシンプルでよいです。

冬なのでアプリは「寒さをみんなでふっとばせ!」にします。ちょっとふっとばすというのは合わないかもしれませんが。アプリ名はsamufutにします。

$ meteor create samufut
$ cd samufut

チュートリアル通りです。ここで、phaserのライブラリを入れてしまいます。

$  meteor add robertlowe:meteor-phaser

これができるようになったのは最近のようですね。

$ meteor &

で起動して準備完了です。コーディング開始。


とりあえず、Phaserを使ったコードを書くところで時間がかかっています。これはあまりMeteorは関係ないところです。 一つだけ気になったこととして、本体のjsを編集すると毎回パッケージのアップデートをしようとして遅いので、Phaserのコードはclient配下に移動しました。この場合はrefreshするだけなので比較的早いです。

Meteorの話が出てくるのは明日になりそうなので、本記事はとりあえず一時保存します。


さて、一日経過しました。その後、Phaserのコードはほぼ完成し、次に通信部分のコードに着手します。Meteorは既にsockjsを使って通信を行う仕組みになっているのですが、それをアプリからも使えるとよいです。調べたところ、

http://arunoda.github.io/meteor-streams/

というのがありました。パッケージはこちらですね。

$ meteor add lepozepo:streams

でインストールします。簡単です。サンプルコードもそのまま貼り付けます。

chatStream = new Meteor.Stream('chat');

if(Meteor.isClient) {
  sendChat = function(message) {
    chatStream.emit('message', message);
    console.log('me: ' + message);
  };

  chatStream.on('message', function(message) {
    console.log('user: ' + message);
  });
}

直感的ですね。これで実装してみましょう。今回はinsecureは入れたままにします。

完成しました。コードは237行でした。 それでは、デプロイしてみます。

% meteor deploy samufut.meteor.com

できました。メールアドレスが要求されました。

しかし、このURLはgithub pagesやherokuと同じように問題になるのではないでしょうか。そのうち、*.meteorapp.comとかになるかもしれませんね。

デプロイされたサイトは、

http://samufut.meteor.com/

です。ぜひ、遊んでみてください。使い方は書きませんでしたので、暑さをみんなでふっとばせ!の方を参照してください。

少し試した範囲だと、環境によっては通信がうまく行かないケースがあるようです。遅延というか、反映されないのです。またの機会に調べましょう。

ソースコードは下記にアップしました。

https://github.com/dai-shi/samufut

当初は、もっとMeteorのことを書こうと思っていたのですが、苦労したのはPhaserまわりだったのであまり書くことがなくなってしまいました。まだ、やったことないですが、insecureを外してまじめに実装するといろいろコード量が増えて、結局大変っていうことにならないか心配です。それでも、プロトタイプが手軽に作れるのは利点だと思います。

気になったことを一つ思い出しました。普段非同期処理で書くことが多く、比較的好んでいるのですが、Meteorのクライアント側のmongoは同期的なので戸惑いました。キャッシュされているからなせるわざなのでしょうか。事前にpublishできないようなデータの場合どうなるかなど気になります。もう少し勉強しなければいけません。

それでは、Meteorの今後に期待します。


12/28追記。

sudoがいらないと思ったのは勘違いでした。たまたま、その時の環境が/usr/local/binにwrite権限があったため。

以前書いた、 AngularJSを使ってRailsより簡単にTwitterクローンを作りたいと思って作ったnode.jsのライブラリ「social-cms-backend」をちょっとずつ改良しています。

今回は、通知機能です。通知機能というのは、例えば、自分のつぶやきに誰かが返信した場合にお知らせしてくれる機能です。今のところ、social-cms-backendはFacebookのみと連携しているので、通知機能もFacebook連携にしようと思いました。

Facebookアプリは何年か前にも一度作ったことがあって、そのときにapp requestsという機能で通知したのを覚えています。ところが、最近ではapp notificationsという機能もあるのですね。

http://developers.facebook.com/docs/games/notifications/

これです。このAPIの方がずっと素直で分かりやすい。難点は現状ではモバイルアプリに対応していないこと。また、通知先もCanvas対応アプリしか使えないようです。Betaとなっているので、将来的には使えることを期待してapp notificationsで行こうと思います。しかし、また数年後には使えなくなっていそう。Facebook依存はそのあたりがつらいので、social-cms-backendの通知機能としてはFacebook連携以外もサポートしていきたいところです。

APIは/<userid>/notificationsにHTTP POSTするだけです。

facebook-node-sdkを使った呼び出しサンプルコードは次のようになります。

FB.api(facebook_user_id + '/notifications', 'post', {
  href: href,
  template: template
}, function(res) { 
  //result handling
});

hrefはジャンプ先の相対URL、templateにはメッセージを入れます。これをちゃんと動かすにはapp access tokenが必要ですが、その方法は、

HerokuでFacebook APIを使う時の注意点

を参照してください。

いかがでしょうか、簡単でしょう?

ttyrec/jsttyplayがうまくいったので、ちょっと欲が出てきて、GitHubのプロジェクトのREADMEに作ったスクリーンキャストを載せたくなりました。

Markdown自体はHTMLタグを埋め込むことができるのですが、README.mdが表示されるときには全てのタグが使える訳ではありません。

ざっと試したところ、script,canvas,iframeは消えました。たぶん、embedも消えるでしょう。

最後の手段は、画像です。画像は載っているのを見たことがあります。 そこで、スクリーンキャストをGIFアニメーションに変換しようと思いました。

ttyrecは文字情報が記録されているので、そこから直接GIFアニメーションに変換できたら効率的な(サイズの小さい)ファイルが作れるはずだ、と期待したのですが、残念ながらそのようなツールは見つかりませんでした。

仕方ないのでテキストベースはあきらめ、普通に画面キャプチャすることでスクリーンキャストする方式に戻ることにしました。 GIFアニメーションを出力できるスクリーンキャストツールとしては、 Screencast-O-Maticがお手軽そうだったので試してみました。

まず、初めに普通にターミナルを録画して変換してみましたが、GIFファイルが大きくなりすぎました。そこで、フォントを小さくして、さらにttyrecで記録したものを倍速で再生することで時間も半分にしたところ、だいぶ小さくはなりました。それでも時間が長いと大きいですが。

これを、README.mdに埋め込むのですが、ファイルはどこに置くかというと、 GitHub pagesに置きました。

ところが、まだファイルが大きすぎるのかうまくいきません。Not Foundになってしまいました。仕方ないのでraw.github.comを使ってファイルの位置を指定したら、今度はうまく行きました。ちなみに、サイズが大きすぎるとblob is too bigというエラーがでます。

本来はraw.github.comを使ってコンテンツ配信をするのはよろしくないのだと思います。ファイルサイズをもう少し小さくすることができたら、GitHub pagesのリンクも再挑戦することにします。

最近、ブログネタがどんどんマイナー路線に行ってしまっていますが、だれか読者はいるでしょうか。


8/2追記。

4倍速にして時間を圧縮し、fpsを半分にして、ファイルサイズを小さくしたところ、GitHub pagesのリンクでも動くようになりました。 これにより、GitHub pagesを開く際はそのままリンクが使われ、github.comを開く際は、よろしくCDNにコピーされるようになりました。

動画は便利ですね。身体的な技を習得する場合もそうですが、最近ではプログラミングも動画で習得するようですね。自分は使っていませんが、ドットインストールは便利そうです。

さて、本題です。スクリーンキャストをテキストベースで行いたいと思いました。ターミナルだけで手軽にやろうと。

ttyrecというツールを使いました。Ubuntuではaptでインストールできます。

$ apt-get install ttyrec

実行すると新しいシェルが始まるのでそこで一通り作業してexitすると、全て記録されています。とても簡単です。

ttyplayで実行できます。再生中に速度も変えられるので便利。2倍速くらいでもついていけます。

ttyrecで録画したスクリーンキャストをwebにアップする方法を調べました。2つ見つけました。

  • playterm.orgというサイトにアップする
  • jsttyplayというライブラリを使う

初めはお手軽なplayterm.orgを使うつもりでしたが、一つだけ難点が。試してないから確かではないのですが、一度アップすると削除ができない様子なのです。もうちょっと自分で制御したくて、jsttyplayを使うことにしました。しかし、jsttyplayはライブラリなのでいろいろ手を入れないと使えません。まずは、Webサイトが必要です。

今回は、GitHub pagesを使いました。gh-pagesブランチにjsttyplayをコピーして、test.htmlを参考にHTMLを書きました。不具合もいくつかあって、

  • Rewindボタンが動かない(バグ修正した)
  • PlayPauseボタンの動作が不明(非表示にした)
  • fontが相対パスなので、ディレクトリが異なると読み込めず
  • ttyrecordファイルのURLがハードコードされている

などが気になりました。

あと、注意点がいくつかあります。

  • ターミナルのサイズを記録時と再生時で同じにする必要がある。これは、ttyrecの制限。playterm.orgでは80x24を標準としている。
  • TERM=xtermでは途中で固まった。TERM=vt100だと大丈夫。ttyplayは問題ないので、これはjsttyplayの制限。

これらをクリアして見事ttyrecで記録したスクリーンキャストをWebに載せることができました。HTML5 Canvasで動いているようです。ターミナルをエミュレートしている感じでしょうか。かっこいいですね。

jsttyplay_screenshot.png

このシンプルさがいいです。 再生速度変更ボタンはありませんが、簡単に作れそうです。 そのうち暇ができたらちょっと改造してみようかと思いました。

興味がある方はお試しください。

RSS Pipesの補完機能としてのRSSリーダーを、HTML5 Canvasのみを使ってどこまでスマホアプリが作れるのか挑戦中です。

Googleリーダがなくなるから、それの代替として作ろうと始めましたが、Googleリーダの期限までには完成しそうにありません。

今日は、RSSの記事のリンクを開く機能を作りました。 CanvasではHTMLは表示できません。そこで、Canvasの外側に独立して iframeを使うことで表示しました。 iframeの表示はjQueryを使いました。KineticJSのようなpure JavaScriptを使っていると、DOM操作はちょっと古びたものに感じます。

さて、RSSのリンクを開くためのボタンも作ってみました。 右上の方に矢印のアイコンを置いて、それを押すとiframeが開きます。 その矢印アイコンですが、Kineticのshapeで作成するのは面倒だったので、inkscapeで作成してSVG DataをKineticのpathで読み込みました。

コードはこんな感じです。

var path = new Kinetic.Path({
  x: 0,
  y: -6,
  data: 'M 23.618434,50.171286 41.144607,36.346769 23.23155,22.400304 l 0.0092,8.18132 c 0,0 -7.445838,-1.03921 -11.864782,3.095364 -4.4188829,4.134559 -4.3930939,13.742678 -4.3930939,13.742678 0,0 2.442808,-4.269676 6.9240349,-5.841822 4.481182,-1.572147 9.583341,-0.746586 9.583341,-0.746586 l 0.127874,9.340028 z',
  fill: '#f0f0f0',
  scale: 0.43
});

全コードはこちらです。

https://github.com/dai-shi/canvas-rss-reader/

実行サンプルはこちらです。

http://canvas-rss-reader.herokuapp.com/

未読既読の機能までできたら、とりあえずは使えるようになるかと思うのですが、どうでしょう。

RSS Pipesの補完機能としてのRSSリーダーを、HTML5 Canvasのみを使ってどこまでスマホアプリが作れるのか挑戦中です。

今回は、フリックで横スライドに挑戦しました。 しばらく時間が空いてしまったのは、どうやって実装するかイメージがわかなかったからです。 試行錯誤した結果、比較的満足いくものになりました。

実装するときに気にしているのは、 普通のスマホアプリと同じくらいの操作性を目指しつつ、KineticJSの機能をフル活用して少ないコーディングで実現することです。 横スライドも、縦スクロールと同様にドラッグ&ドロップの機能を活用しました。 なので、正確にはフリックを判定しているわけではなく、ドロップ後の位置でスライドするかどうか判定しています。 ちょっと使ってみると、直感と異なることもあるのですが、それは今後の課題ということで。

今回、Canvasでアプリを作る時に意識をしているのは、軽快さです。 普通に作ったアプリより軽く感じるWebアプリを作れるのかがポイントです。 現状はまだ機能が少ない(そもそも、まだRSSのURLが開けない)ですが、 自分のスマホではかなり軽快にスライド動作ができるようになっています。

他のスマホではどうなのか気になります。 ぜひ、お持ちのスマホで試してみてください。 下記の実行サンプルをスマホのブラウザで開くだけです。

http://canvas-rss-reader.herokuapp.com/

全コードはこちらです。

https://github.com/dai-shi/canvas-rss-reader/

この時点のコードを参照するには、本日のコミットを参照してください。 上記実行サンプルは、今後最新版に変わっていってしまいますので、ご注意を。


6/23追記。

大切な発見を書くのを忘れていました。TweenのonFinishは呼ばれないことがあってはまりました。Tweenでノードを動かしている時に、ドラッグ&ドロップをすると、終了時にonFinishが呼ばれませんでした。setTimeoutを使うことで解決しました。KineticJSのバージョンは、4.5.2です。この仕様が将来のバージョンでどうなるかは不明です。

RSS Pipesの補完機能としてのRSSリーダーを、HTML5 Canvasのみを使ってどこまでスマホアプリが作れるのか挑戦中です。

以前、RSS取得を実装したので、今回はそれっぽく表示してみることにしました。

まずは、RSSのタイトルと概要をリスト表示するだけです。 一つのアイテムあたりに80pxの高さを使いました。

KineticJSなのかCanvasなのか分かりませんが、フォントサイズをpxで指定するため、手軽にレイアウトできました。

スクロール処理もちゃんと動き、上々です。

しかし、改行の処理が甘いですね。甘いというレベルではなくおかしいですね。このあたり、Canvasでアプリを作るときのつらさかもしれません。はまりそうなので、今回は手を出さないことにしました。

今回は実行結果のスクリーンショットをとりました。

canvas-scn.png

全コードはこちらです。

https://github.com/dai-shi/canvas-rss-reader/

実行サンプルはこちらから。

http://canvas-rss-reader.herokuapp.com/

どのRSSフィードを表示するかもクエリパラメータでURL指定できるのですが、詳しい話はまた今度にします。

RSS Pipesの補完機能としてのRSSリーダーを、HTML5 Canvasのみを使ってどこまでスマホアプリが作れるのか挑戦中です。

まだ、スクロールの処理を実装しています。KineticJSでできるだけ簡単にやろうと考えて、ドラッグ&ドロップのイベントをつかってやりました。

スマホで使うには、やはり慣性スクロールが重要です。正しい呼び方は知りませんが、あの指を離してもしばらくスクロールしているあれです。

まじめに物理シミュレーションをしようかとも思ったのですが、使いやすければいいだろうということで感覚的にパラメータ調整しました。KineticJSではここらあたりが限界でしょうか。

ちなみに、iPhoneの複数のアプリでスクロール操作を試してみましたが、アプリによって挙動が違うことに気づきました。例えば、Safariではスクロールが止まるのが早いですが、Twitterではしばらくスクロールしています。今まであまり意識していなかったので気づかなかったです。

あと、スクロール途中にタッチして止めることが相当重要だとうことを再認識しました。

コードは少し長くなってしまったので載せません。あまり特記するポイントはないです。バウンスするところと組み合わせるのがすこしやっかいでした。

全コードはこちらです。

https://github.com/dai-shi/canvas-rss-reader/

実行サンプルはこちらから。

http://canvas-rss-reader.herokuapp.com/

うまくいったらライブラリ化したいと思いつつも、今は決めうちのコードになっています。

RSS Pipesの補完機能としてのRSSリーダーを、HTML5 Canvasのみを使ってどこまでスマホアプリが作れるのか挑戦中です。

今回は、スクロール時のバウンスに挑戦です。スクロールの端で戻るやつです。

米特許商標局、アップルのバウンススクロール特許は無効と予備的判断。対サムスンでも争点

こんなニュースになっているやつです。

さて、KineticJSにはTweenという機能があって、スムーズな動きが簡単に表現できます。ちなみに、Tweenというのは、

http://en.wikipedia.org/wiki/Inbetweening

のことのようですね。 例によって、 KineticJSのチュートリアル を見ながらやったのですが、一つはまったことがあります。

最初にどのドキュメントをみたのか定かではありませんが、今日までKineticJSのv4.4.2を使ってました。しかし、このバージョンではチュートリアル通りのコードを実行するとエラーになります。最新のv4.5.2を使ったら解決しました。

コードはこんな簡単に書けてしまいました。すごいお手軽。

layer.on('dragend', function() {
  var pos = layer.getPosition();
  var newY;
  if (pos.y > 0) {
    newY = 0;
  } else if (pos.y < -layerHeight + stage.getHeight()) {
    newY = -layerHeight + stage.getHeight();
  } else {
    return;
  }
  var tween = new Kinetic.Tween({
    node: layer,
    easing: Kinetic.Easings.StrongEaseOut,
    duration: 0.3,
    y: newY
  });
  tween.play();
});

今回も、実行結果はスクリーンショットでは見せられないので、下記からどうぞ。アニメーション系はスクリーンショットは使えませんね。動画にするのは手間がかかりすぎる気がします。

http://canvas-rss-reader.herokuapp.com/

全コードはこちらです。

https://github.com/dai-shi/canvas-rss-reader/tree/adfe56808e98da9936c690b85bd169aabca4bb13

コミットの日付を見れば分かるでしょうということで、次回からはコミット指定のURLの記載は省略します。

RSS Pipesの補完機能としてのRSSリーダーを、HTML5 Canvasのみを使ってどこまでスマホアプリが作れるのか挑戦中です。

前回の結果、スクロール動作が遅いことが気になりました。

KineticJSを使うとノードの操作やイベント処理などは格段に使いやすくなるものの、描画という点ではオーバーヘッドになっているようです。おそらく、ノード数が増えれば増えるほど遅くなるのでしょう。

http://www.html5canvastutorials.com/kineticjs/html5-canvas-shape-caching-with-kineticjs/

に画像としてキャッシュすることで、高速化する方法が記述されていたので試してみました。その結果、だいぶ改善されました。これなら実用に耐えそうです。しかし、逆にノード毎のイベントハンドラーが使えないことになります。せっかくのKineticJSですが、活用できていないことになってしまうのかもしれません。このあたりは引き続き考えてみましょう。

コードはこんな感じになりました。

function updateRssContent(items) {
  var tmp = new Kinetic.Layer();
  var rect = new Kinetic.Rect({
    x: 0,
    y: 0,
    width: stage.getWidth(),
    height: stage.getHeight(),
    fill: '#000000'
  });
  tmp.add(rect);
  var y = 5;
  $.each(items, function(index, item) {
    var text = new Kinetic.Text({
      x: 5,
      y: y,
      text: item.title,
      fontSize: 12,
      fontFamily: 'Arial',
      fill: '#aaaaff'
    });
    y = y + text.getHeight() + 5;
    tmp.add(text);
  });
  if (y > rect.getHeight()) {
    rect.setHeight(y);
  }
  tmp.toImage({
    width: rect.getWidth(),
    height: rect.getHeight(),
    callback: function(img) {
      var image = new Kinetic.Image({
        image: img,
        x: 0,
        y: 0
      });
      layer.add(image);
      layerHeight = rect.getHeight();
      layer.draw();
    }
  });
}

実行結果はスクリーンショットでは見せられないので、 下記からどうぞ。ただし、数日間以内には新しいバージョンになってしまいます。昨日のバージョンはたった1日の公開でした。

http://canvas-rss-reader.herokuapp.com/

全コードはこちらです。

https://github.com/dai-shi/canvas-rss-reader/tree/9061aeab3ab034beca7fdaa9a472d3f3225671d5

必要であれば、このコミットをチェックアウトして自力で走らせることもできます。