0

Sinatra RocketIOでお絵かき共有作った

横浜から電車に座れたので、学校行くまでの間にスマホやタブレットやPCで同時に絵を描けるやつをcanvasとSinatra RocketIOで作った。
一瞬でできた。RocketIOの生産性はヤバイ。

デモ http://canvas.shokai.org/ch1
URL末尾がRocketIOのチャンネルになっていて、同じURLを開けばどのブラウザからでも同じ絵が同時に編集できる。


Nexus7で絵を描いてMacでgyazoすると便利。


チャンネルは http://canvas.shokai.org/shokai
とか http://canvas.shokai.org/hogehoge
とか好きなだけ増やせる。


ソースコード
shokai/rocketio-canvas · GitHub


デモなので突然止めたりしますが、ソースをgit cloneしてきてbundle installしてrackupすればRuby1.8でも2.0でも動くのでどうぞ。

ご意見ご要望などあったらissueやtwitter @shokaiへ


実装はとても簡単で、一番面倒だったのはcanvasをスマホでもPCでも使えるようwrapするjs部分だった。
DBは使っていない(オンメモリ)

READMEやGemfile含めて300行ちょっとで完成した。

% git ls-files | xargs wc -l
       4 .gitignore
7 Gemfile
77 Gemfile.lock
1 Procfile
31 README.md
8 config.ru
50 main.rb
62 public/canvas.js
40 public/index.js
51 views/index.haml
331 total

0

canvasで画像処理

html5のcanvasのピクセル単位の処理を試してみた。ここで動かせる → http://dev.shokai.org/test/canvas/cv/

ソースはここ https://github.com/shokai/js-canvas-test/tree/master/cv


輪郭抽出とかしてみた
html5 canvas edge detect


■canvasでのピクセル処理方法
canvasはgetContext(‘2d’)してからgetImageDataするとImageDataオブジェクトが手に入る。
あとの処理はこんな感じ(画像をグレースケールにする例、cv/main.jsの39行目あたり

var canvas = $('canvas#img');
var ctx = canvas[0].getContext('2d');

var img = ctx.getImageData(0, 0, canvas[0].width, canvas[0].height);
for(var i = 0; i < img.data.length; i+=4){
var r = img.data[i]&0xFF;
var g = img.data[i+1]&0xFF;
var b = img.data[i+2]&0xFF;
// var a = img.data[i+3]&0xFF;
var gray = (r+g+b)/3;
img.data[i] = gray;
img.data[i+1] = gray;
img.data[i+2] = gray;
}
ctx.putImageData(img, 0, 0);
データが1次元配列になっていて、RGBAの順に値が格納されている。なのでImageData.dataの要素数はピクセル数の4倍になる。
x,y座標でデータを取れる関数が無いので、自分で色々試して作ってみたけどどうしても3〜6倍ぐらい遅くなってしまうので1次元配列のまま扱う事にした。
JavaScriptで多値を扱うには配列やオブジェクトを使わなければならないんだけどどうやらそれらの生成コストがかなり高いらしく、1ピクセルずつ処理したらひどく遅くなった。


■クロスドメイン
別ドメインの画像をcanvasに読み込んで、getImageDataはできない。chromeだと “Uncaught Error: SECURITY_ERR: DOM Exception 18” というエラーが開発パネルに出る。
ローカルでの開発時にローカルのhtmlからローカルの画像を読み込んでgetImageDataしてもこのセキュリティエラーが出るので、rubyで簡単なhttpサーバーを作っておいたのでコレ使うといい。
実行するとそのディレクトリでhttpサーバーが起動する。


■実行速度
端末によって実行速度が違う。

下の画像は、今年の2月に買った11インチMacbook Airのchromeで、1000×1000の画像を4段階量子化したところ。
だいたい50ミリ秒で量子化できた。遅いパソコンでも毎秒数回は実行できそう。
iPodTouch 4Gや初代iPadはMacbook Airより30倍ぐらい遅くて2秒弱かかる。Nexus Sが意外なことに更に遅くて25秒ぐらいかかる。Macの500倍遅い。
html5 canvas qunatize

0

canvasとwebsocketでお絵かき共有

http://canvas.shokai.org

HTML5のcanvasを使ってみたかったので作った。
websocketも使っているので、chromeかsafariで動く。ブラウザ2つ開いてみると、同期しているのがわかりやすい。

画像のURLを末尾につけるとその画像が読み込めて、マウスで線を引くとみんなでリアルタイムに描ける。Gyazoと合わせて使うと便利。
http://canvas.shokai.org/http://gyazo.com/365d02afdf4953d40ec904df5019aa13.png


ソースはgithubに置いた https://github.com/shokai/shared-canvas


こんな風に、オンラインゲームのマップを置いて、友達と「ここ攻めろ」みたいな指示を共有したいなと思って作ってみた。
http://canvas.shokai.org/http://gyazo.com/1b2f1f6b0df60aa2d0dfe3da79106a4e.png



canvasのプラグインは色々あるけど、canvasのAPIの操作感が変わりすぎる物ばかりだったので、とりあえず何も使わないで操作してみた → draw.js

Imageオブジェクトで画像を読み込んで、onloadイベント発生時にcanvasのサイズを変更するとぴったりのサイズになる。かならずdrawImage前にサイズ変更をすること。
92行目あたり

var draw_img = function(img_url, onload){
var img_tag = $('canvas#img');
ctx = img_tag[0].getContext('2d');
var img = new Image();
img.onload = function(){
img_tag.attr('width', img.width).attr('height', img.height);
ctx.drawImage(img, 0, 0, img.width, img.height);
if(onload && typeof onload == 'function') onload();
};
img.src = img_url; // 読み込み開始
};


rubyとem-websocketで作ったwebsocketサーバーは、daemontoolsで自動起動・復活するようにしてある。DBは無くて、最近10万本のlineの色と太さと座標データを保存している。
また線にはどの画像URLの線か、というデータも付いているので、これで画像URL毎に部屋分けを行っている。