「RSS」と一致するもの

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

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

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

前回に続き、スクロールを実装しました。

その前に、テキストの配置について補足します。前回は、setOffsetを使いました。ちょっと分かりにくかったので、ループの中でy座標をインクリメントするように変更し、背景の高さもあわせて大きくするようにしました。詳しくはコードを参照してください。

さて、本題のスクロール処理についてです。スマホを想定しているので、スクロールバーではなく、タッチ(ドラッグ)スクロールです。はじめは、touchstart/mousedownイベントを使って自力でスクロール処理を書いてみましたが、スクロールするたびにdraw()で再描画をしたところ、とても遅くなってしまいました。

そこで、KineticJSが用意しているDrag&Dropの仕組みを使う方法に変更しました。この方がすっきり書けました。どこまで細かい制御ができるかはこれからのお楽しみです。

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

var layer = new Kinetic.Layer({
  draggable: true,
  dragBoundFunc: function(pos) {
    var newY = pos.y;
    if (newY > 50) {
      newY = 50;
    }
    var minY = -50 - rect.getHeight() + stage.getHeight();
    if (newY < minY) {
      newY = minY;
    }
    return {
      x: this.getAbsolutePosition().x,
      y: newY
    };
  }
});

想像していたより、スクロールがスムーズではありません。Canvasを直接操作した場合と比較していませんが、KineticJSのオーバーヘッドがそれなりにあるのでしょうか。

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

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

全コードはこちらです。

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

ところで、スマホで試したら、最後までスクロールできないみたいです。また、スクロールも遅いです。早速、課題浮上かもしれません。続く。

だいぶ間が空いてしまいましたが、HTML5を使ってどこまでスマホアプリが作れるのか、挑戦しようと進めています。しかも、フルキャンバスで。

題材はRSSリーダーです。RSS Pipesの補完機能として考えています。

前回はHellow Worldを表示するところまででした。今回は、RSSを取得してテキスト表示してみました。ただし、まだスクロールできません。当然クリックもできません。

RSSはjQueryを使って読み込むことにしました。コードはこんな感じです。

$.ajax({
  type: 'GET',
  url: rssurl,
  dataType: 'xml',
  success: function(xml) {
    var $xml = $(xml);
    var items = [];
    $xml.find("item").each(function() {
      var $this = $(this);
      var item = {
        title: $this.find("title").text(),
        link: $this.find("link").text(),
        description: $this.find("description").text(),
        pubDate: $this.find("pubDate").text()
      };
      items.push(item);
    });
    updateRssContent(items);
  },
  error: function() {
    alert('failed to get the rss: ' + rssurl);
  }
});

updateRssContent()でキャンバスにかきますが、座標も自分で計算しないといけません。

function updateRssContent(items) {
  $.each(items, function(index, item) {
    var text = new Kinetic.Text({
      x: 5,
      y: 5,
      text: item.title,
      fontSize: 12,
      fontFamily: 'Arial',
      fill: '#aaaaff'
    });
    text.setOffset({
      y: -index * (text.getHeight() + 5)
    });
    layer.add(text);
  });
  layer.draw();
}

setOffsetを使うよりいい方法があるのではないかと想像しますが、まだ探していません。

実行結果はこんな感じです。

実行結果20130519

最新版はherokuでも実行可能です。

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

今回は、GitHubにコードをアップしました。

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

そう、プロジェクト名もcanvas-rss-readerにしました。

しかし、キャンバスを使うとあたりまえですが文字列選択ができないですね。JavaScriptでクリップボードって制御できるのでしょうか。

http://www.w3.org/TR/clipboard-apis/

このあたりをウオッチすることになるのでしょうか。

大変、失礼しました。

Gunosy RSSがGoogle Readerで更新されていないなぁ、と思ってはいたのですが、もしや!と思ったら、案の定エラーを吐いてました。

さかのぼること4/15からですね、エラーが出ていたのは。本家GunosyのUIデザインが変わっていたのですね。そういうことですか。

幸い、少ない修正で対応できました。(JSONPath)[https://github.com/s3u/JSONPath]というライブラリのおかげです。興味がある方はソースを見てみてください。

https://github.com/dai-shi/gunosy-rss

しかしながら、非公式サービスというのはこういうときに弱いですね。本家Gunosyが学習できるURLでRSS配信してくれればいいのですが、やはりRSSユーザはマイノリティですかね。

RSS Pipesの一機能として、スマホ向けのRSSリーダーみたいなものを作ってみようかと思います。

まずは、KineticJSの勉強をしているところです。 HTML5 Canvas KineticJS Tutorials を参考にしながら作っていきます。

HTMLのコードを説明します。

初めに、KineticJSの読み込みます。

<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.4.2.min.js"></script>

試しに使う場合はこのままでいいでしょう。デプロイするときはローカルに持ってこようと思います。

次に、自分のスクリプトを読み込みます。

<script src="test.js" defer="defer"></script>

deferを指定しておくことで、HTMLのロード後に読み込まれます。

空のbodyにIDを指定しておきます。

<body id="container"></body>

こうしておくことで、KineticJSのstageをbodyの直下に作ります。

さて、ちょっとだけCSSが必要です。

html, body {
  margin: 0;
  padding: 0;
  overflow: hidden;
}

これを指定しないと、Canvasの周りにマージンができてしまって、Full Canvasになりません。また、overflowを指定しないと、ChromeやFirefoxでスクロールバーが出てしまいました。

続いて、本題のJavaScriptのコードを説明します。

KineticJSのステージを作ります。

var stage = new Kinetic.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight
});

widthとheightを表示領域一杯にすることで、Full Canvasとなります。

レイヤーを作ります。

var layer = new Kinetic.Layer();

表示領域一杯の四角形オブジェクトを作ります。

var rect = new Kinetic.Rect({
  x: 0,
  y: 0,
  width: stage.getWidth(),
  height: stage.getHeight(),
  fill: 'black'
});

黒にしてみました。

Hello Worldの文字オブジェクトを作ります。

var text = new Kinetic.Text({
  x: stage.getWidth() / 2,
  y: stage.getHeight() / 2,
  text: 'Hello World!',
  fontSize: 30,
  fontFamily: 'Arial',
  fill: 'lightgray'
});

ステージのサイズから計算して中央に配置しています。x,yは文字オブジェクトの左上の座標です。そこで、ちょっとずらして文字オブジェクトの中心に合わせます。

text.setOffset({
  x: text.getWidth() / 2,
  y: text.getHeight() / 2
});

最後に、四角形オブジェクトと文字オブジェクトをレイヤーに追加して、そのレイヤーをステージに追加して、終わりです。

layer.add(rect);
layer.add(text);
stage.add(layer);

これで、Canvasのみを使ってHello Worldを表示するページを作ることができました。アプリと呼べるようになるまでにはまだまだかかりそうですね。

実行結果はこんな感じです。

コードはGistにアップしておきました。 https://gist.github.com/dai-shi/5404537

RSS Pipesの一機能として、スマホ向けのRSSリーダーみたいなものを作ってみようかと思います。

RSSリーダーなんて世の中にたくさんあるので、普通のものを作っても面白くありません。そこで、勉強がてらHTML5でスマホアプリにしてみようと考えました。しかも、フルキャンバスで。canvasというのはグラフィック用の機能ですが文字も書けるわけで、それですべてを作ったら面白いのではないかと思いました。

さて、HTML5 Canvasは何年か前に少しだけ触りましたが、最近はライブラリも充実しているのではないでしょうか。検索してみると、

HTML5のcanvasを劇的に使いやすくするJavaScriptライブラリまとめ5つ

を見つけました。また、stackoverflowで、

Current State of Javascript Canvas Libraries(2012)?

を見つけました。使ってみないと分からないだろうと思いますが、これらの情報から、KineticJSを試してみることにしました。

今日のところはここまで。これから少しずつ進捗を書いていけたらいいなと思っています。

RSS Pipesは、JavaScriptでフィルターを書く、RSSアグリゲーターです。

RSSの登録数が増えてきたら検索機能を作ろうと思っていたのですが、 先に作ってしまいました。検索と言っても、表示されているものを絞り込むだけです。

AngularJSには標準でフィルターというものが組み込まれているので非常に簡単にできました。ソースコードに検索ボックス配置の1行と、フィルター追記の1行で済んでしまいました。Bootstrapのレイアウトでもう数行いじりましたが、トータルで5行の改変のみです。

興味ある方は、diffを見てみてください。

https://github.com/dai-shi/rss-pipes/commit/c250c8a7d9b03476503c78fda5a2f0ab5cef5894#views/partials/home.jade

お手軽です。詳しくは、AngularJSのチュートリアルを参照しましょう。

http://docs.angularjs.org/tutorial/step_03

RSS Pipesは、JavaScriptでフィルターを書く、RSSアグリゲーターです。

フィルターJavaScriptはブラウザ上で誰でも書くことができます。すなわち、バグ入りのコードや、悪意のあるコードも書くことができます。 そのような不正なコードにどう対処するかがポイントになります。

RSS PipesはNode.jsで動いています。Node.jsにはvmというモジュールがあって、サンドボックス環境を作ることができます。 Node.jsを使ったことがある方は分かると思いますが、モジュールを書いてrequireするとexportsしたもの以外は名前が衝突しません。 これはrequireでモジュールをロードする時にサンドボックス環境で評価しているからです。 RSS Pipesでもこの仕組みを使ってJavaScriptのコードを評価しています。 よって、ブラウザ上から入力されたJavaScriptコードによって、サーバ側のNode.jsにアクセスされることはありません。

ところが、vmモジュールのマニュアルをよく読むと書いてあるように、このモジュールを使うことで安全になるわけではありません。安全にコードを走らせるためにはサブプロセスを使うように、と書いてあります。

どういうことかと言うと、Node.jsはシングルスレッドで動作するため、vmでサンドボックス環境を作ったとしてもそこで使用されるリソース(CPUパワーやメモリ)を制限することはできず、リソースを不用意に使われてしまう可能性があります。簡単な例では、

while(true) {}

というJavaScriptを走らせるとNode.js全体が固まってしまいます。 とは言っても、herokuで動いているRSS Pipesでサブプロセスを使うわけにはいきません。(もしかして、使うこと自体はできるのかも?)

そこで、無限ループを判定するような仕組みをいれています。 先ほどの例では、

while(true) {
  counter++;
}

のようにして、counterが一定値を越えた場合に強制的に止めればよいのです。これで、どの程度の危険が回避できたかは定かではありません。正確にCPUの使用率を測っているわけでもありませんし、メモリについてはノータッチです。それでも、無限ループで固まることは回避できているので、少なくとも悪意のないコードでしたら、問題のない範囲と思っています。

将来的に、vmモジュールのサンドボックス環境でリソース制限までしてくれるようにはならないでしょうかね。ちょっと興味あります。

まだ誰にも使われていないRSS Pipesですが、機能を追加しました。

RSS PipesはRSSのアグリゲーターでフィルターをJavaScriptで編集します。編集はWebブラウザ上で行うので、いわゆるWikiのようなシステムです。

誰でも編集できてしまうので、そのうちSPAM行為をする利用者もでてくるかもしれません。まあ、そうなるくらいになったらうれしいわけですが。

SPAM行為をされてから対策すればよいと思っていたですが、SPAM対策がされていないことで、利用者が躊躇してしまうのは本望ではありません。そこで、簡単なロック機能を作りました。

編集するときにロックコードを入力すると、以降そのロックコードを入力しないと更新できなくなります。もちろん、ロックコードを忘れたら、本人でも更新できなくなります。そういうときは、あきらめて新しいフィルターとして再登録しましょう。「複製」ボタンがあるので、簡単です。

http://dai-shi.github.io/rss-pipes/

RSS Pipesを使ってJavaScriptの勉強をしよう、の第三弾です。

昨日、はてなブックマーク新着エントリからキーワードでフィルタリングするRSS Pipesの例を紹介しましたが、はてなブックマークの場合ははてなが提供するRSSでキーワードフィルタリングできてしまいます。

そこで今日は、キーワードが含まれていた場合タイトルを書き換える方法を例題にします。

フィードを流し読みするときは、タイトルに気になるキーワードが入っていると目に留まりませんか。自然と目でキーワードフィルタリングしているのかもしれません。そこで、それを助けるためにタイトルを強調表示してみましょう。★マークをつけるのはどうでしょうか。

次のようなJavaScriptコードになります。

function rssPipesFilterFunction(articles) {
  var keywords = ["JavaScript", "javascript"];
  var i, len = keywords.length;
  articles.forEach(function(article) {
    if (article.title) {
      for (i = 0; i < len; i++) {
        if (article.title.indexOf(keywords[i]) >= 0) {
          article.title = "★★★ " + article.title;
          break;
        }
      }
    }
  });
  return articles;
}

前回と同じように、indexOfでキーワードを探して、見つかった場合はarticle.titleに文字列を追加しています。

RSS Pipesで動くサンプルはこちらです。

RSS Pipes: hatena/JavaScript関連のブックマーク/タイトル強調表示

もう、ネタがなくなってきたかも。