ブラウザでお絵描きプログラミング! Processing.js 登場!
はじめに
今日、 jQuery の作者として有名な John Resig さんが Processing.js という JavaScript のライブラリを公開しました。
John Resig - Processing.js
このライブラリを使うと、比較的簡単に以下のようなグラフィックスやアニメーションを書くことができるようになります。
というわけで、公開されたばかりのこのライブラリを簡単な使い方から詳しい使い方までとことん掘り下げてみたいと思います。
Processing.js 概要
まず、 Processing.js とは何かという話をします。
Processing.js とは、ブラウザで Processing というプログラミング言語を実行する JavaScript のライブラリです。
では、 Processing とはどのようなプログラミング言語なのでしょうか。
Processing とは
Processing とは、ビジュアルデザインのためのプログラミング言語で、 Java のような文法をしています。
ビジュアルデザインのためのプログラミング言語というと難しそうですが、実際はとても簡単です。
たとえば、以下の 2 行を書けば、丸が書けてしまいます。
size(200, 200); ellipse(100, 100, 100, 100);
size(200, 200); background(0); noStroke(); for (int i = 0; i < 100; i ++) { fill(random(255), random(255), 255, 100); ellipse(random(200), random(200), 60, 60); }
Processing.js を使うための準備
Processing.js を使うための準備をしましょう。 Processing.js を使うには、以下の手順が必要です。
- Processing.js をダウンロードする。
- HTML ファイルを作って、Processing.js を読み込む。
- canvas 要素を挿入する。
- Processing という関数を呼び出す。
ちょっとめんどくさそうですが、やってみると簡単です。順番に細かく説明します。
Processing.js をダウンロードする。
以下の JavaScript ファイルをダウンロードします。
http://ejohn.org/apps/processing.js/processing.js
HTML ファイルを作って、 Processing.js を読み込む
以下のように、HTML ファイルを作って script 要素の src 属性にさっきダウンロードしてきた processing.js のファイルを指定します。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"> <head> <title>Processing Sample</title> <script type="text/javascript" src="processing.js"></script> </head> <body> <h1>Processing Sample</h1> <div> </div> </body> <html>
canvas 要素を挿入する
さっきの HTML ファイルに canvas 要素を挿入します。 width 属性と height 属性で canvas 要素の大きさを指定します。
: <body> <h1>Processing Sample</h1> <div> <canvas width="400" height="400"></canvas> </div> </body>
サンプル
ちなみに、 canvas 要素とは、 JavaScript から絵を描ける要素です。また、 IE には canvas 要素はありませんが、 excanvas.js を読み込むことで canvas 要素を使えるようになります。
Processing という関数を呼び出す
Processing.js によって Processing 関数が作られているので、 JavaScript から Processing 関数を呼び出します。
引数には、
を指定します。
: <title>Processing Sample</title> <script type="text/javascript" src="processing.js"></script> <script type="text/javascript"> // ロードされた時に実行 window.onload = function() { // canvas 要素 var canvas = document.getElementsByTagName('canvas')[0]; // Processing のソースコード var code = "size(400, 400); noStroke(); fill(255, 0, 0, 255 * 0.5); ellipse(200, 150, 200, 200); fill(0, 255, 0, 255 * 0.5); ellipse(250, 250, 200, 200); fill(0, 0, 155, 255 * 0.5); ellipse(150, 250, 200, 200);"; // Processing 関数を呼び出す Processing(canvas, code); }; </script> </head> :
サンプル
これで、丸が描かれます。
準備も簡単ですね。
もっと準備(必要な人は)
基本的な準備は完了ですが、もう 2 個テクニックを追加しておきます。
これは、別にやらなくてもいいです。めんどうな人やメリットを感じない人はこの項は飛ばしてください^^;
script 要素に Processing のソースコードを書く
Processing のソースコードを文字列で渡すのはめんどうくさいですね><
というわけで、そんなときは script 要素を使いましょう。
script 要素は、 type 属性に実行できないタイプが指定されると何もしません。逆にそれを利用して script 要素に Processing のソースコードを書くのです。
: <script type="text/javascript" src="processing.js"></script> <script type="text/javascript"> window.onload = function() { var canvas = document.getElementsByTagName('canvas')[0]; // Processing のソースコードが書かれた script 要素 var codeElm = document.getElementById('processing-code'); // 要素の内容を取得する var code = codeElm.textContent || codeElm.innerText; Processing(canvas, code); }; </script> <script id="processing-code" type="application/processing"> // ここに Processing のソースコードが書ける size(400, 400); noStroke(); fill(255, 0, 0, 255 * 0.5); ellipse(200, 150, 200, 200); fill(0, 255, 0, 255 * 0.5); ellipse(250, 250, 200, 200); fill(0, 0, 155, 255 * 0.5); ellipse(150, 250, 200, 200); </script> :
IE にも対応させる
前の項でも、チラっと言いましたが IE は canvas 要素に対応していないので excanvas.js という JavaScript のファイルを読み込んであげる必要があります。
excanvas.js は以下のページからダウンロードできます。
http://sourceforge.net/project/showfiles.php?group_id=163391
ダウンロードした excanvas_0002.zip ファイルを解凍すると、 excanvas.js が入っているので、それを script タグで読み込みます。
: <head> : <script type="text/javascript" src="excanvas.js"></script> : </head> :
サンプル
excanvas.js については、詳しく解説しませんがとりあえず読み込んでおけば間違いありません。 IE 以外の環境では何もしない無害な JS なので、安心です。
ほら、 IE でも動いてますね!
準備完了!何か書いてみよう!
というわけで、ちょっとアニメーションを書いてみました。コメントを参考にしてください。
Particle particles = new Particle[20]; // setup は window.onload のようなもの void setup() { size(400, 400); for (int i = 0; i < particles.length; i ++) particles[i] = new Particle(); } // draw はタイムラインが更新されるために呼ばれる void draw() { for (int i = 0; i < particles.length; i ++) particles[i].update(); } // 線を管理するクラスを作る class Particle { // フィールドの定義 float x, y, xvel, yvel, r, g, b, a; // コンストラクタで、フィールドの初期化 Particle () { x = random(width); y = random(height); r = random(255); g = random(255); b = random(255); a = 0; xvel = random(1) * 10 - 5; yvel = random(1) * 10 - 5; } // draw から呼び出す // フィールドを更新して描画する void update() { if (a > 2 * 3.14) return; float px = x, py = y; x += xvel, y += yvel; yvel += 0.1; if (x < 0 || width < x) xvel *= -1; if (y < 0 || height < y) yvel *= -1; stroke(r, g, b, sin(a += 0.01) * 255); line(px, py, x, y); } }
サンプル
もちろん、クロスブラウザで動きます。(ただ、 IE だと重いです><)
もっと Processing.js のサンプルが見たいかたは、以下に行くといいです。
http://ejohn.org/apps/processing.js/examples/basic/
Processing の文法は、以下にあります。
Language Reference (API) \ Processing 3+
皆さんも、自分で面白いものを作ってみてください!
ここでは、書かなかったですが本物の Processing と同様にマウスとの連携やキーボードとの連携もできますよ!
JavaScript との値の共有
Processing.js では、 JavaScript のグローバル変数やグローバル関数を Processing のグローバル変数、グローバル関数として使うことができます。
たとえば、以下のように JavaScript で定義したグローバル変数 message を Processing から呼び出すことができます。
<script type="text/javascript"> window.onload = function() { var canvas = document.getElementsByTagName('canvas')[0]; var codeElm = document.getElementById('processing-code'); var code = codeElm.textContent || codeElm.innerText || codeElm.text; Processing(canvas, code); }; // この関数を Processing から呼び出す function message(msg) { document.getElementById('message').innerHTML = msg; } </script> <script id="processing-code" type="application/processing"> void setup() { size(400, 400); noStroke(); frameRate(10); } void draw() { fill(0, 0, 0, 100); float r = random(400 / 2), x = random(400), y = random(400); ellipse(x, y, r, r); // ここで JavaScript の関数を呼ぶ message("r = " + r + ", x = " + x + ", y = " + y); } </script>
サンプル
こんな感じになります。 canvas の下に文字列が挿入されていますね。
この仕組みを使うと Processing のアニメーションと、 HTML の連携を簡単に行うことができます。
Processing.js の仕組み
ここからは、 Processing.js の内部の話をします。この項を知らなくても Processing.js は使えるので、全然飛ばしていいです^^;
Processing.js の内部を知る意味
Processing の関数名は rect とか ellipse とか直感的なものが多いです。なので、 Processing 関数名から Processing.js を調べれば、「canvas ではこう書くんだー」ってのがすぐ分かって面白いです。
あと、たまに特定のブラウザで動かないときもあるので、それを調べるときには内部の仕組みを知っておいた方がいいですね^^
Processing.js は全体で 1700 行くらいしかありません、なので全然読めます。
Processing.js の実行の流れ
Processing.js では、以下の流れで処理が進みます。
- Processing 関数が呼び出される
- 内部で Processing 言語のグローバルの名前空間となるオブジェクト p が生成される。
- 名前空間 p に関数やクラス(実際は JavaScript のプロトタイプ)が追加される。
- Processing のソースコードが内部の parse 関数で JavaScript のソースコードに変換される。
- 名前空間 p が with され、そのスコープ内で JavaScript のソースコードが eval される。
- すると、あら不思議。絵が描画される。
では、実際にソースを見てみましょう。
9 - 14 行目: Processing
以下が Processing 関数の全貌です。この関数(実行コンテキスト)では、 this はグローバルオブジェクトになっているので、 Processing はグローバル関数になります。
this.Processing = function Processing( aElement, aCode ) { var p = buildProcessing( aElement ); p.init( aCode ); return p; };
で、 buildProcessing が Processing のグローバル名前空間 p を生成する関数で p.init が Processing → JavaScript のソースコード変換と実行を行う関数です。
ここで注目すべきは、 p を返してるところですね。Processing のグローバル名前空間 p を返しているので JavaScript で Processing の名前空間をすべて使えるわけです。
Hack し放題です!やっほう!です。
32 - 263 行目: parse
この行では、 parse という Processing → JavaScript のソースコード変換を行う関数が定義されています。
window.parse = function parse( aCode, p ) { // (略) // このコードの中で順番に、文字列の replace メソッドによって JavaScript に変換されて行く // 縦に長いコードなので、コメントを追っていけば内容は分かる return aCode; }
第二引数は使ってないです。たぶん、消し忘れですね。
もし JavaScript のエラーが出る場合は、この最後の最後の行に console.log や alert などを差し込むと良さそうですね。
265 - 1693 行目: buildProcessing
Processing のグローバル名前空間 p を作る buildProcessing 関数が定義されています。
function buildProcessing( curElement ){ var p = {}; p.PI = Math.PI; p.TWO_PI = 2 * p.PI; // (略) p.RGB = 1; p.HSB = 2; // (略) p.ellipse = function(x, y, width, height) { // (略) }; // (略) p.init = function(code) { // (略) }; // (略) return p; }
こんな感じで、ただひたすら p に関数やプロパティを追加しまくります。で、その追加された関数や変数が Processing のグローバル関数やグローバル変数になるわけですね。
この p にはものすごい数の関数や変数が定義されます。とくに重要なものを見て行きましょう。
1565 - 1690 行目: p.init
ここでは、実際に変換された JavaScript のコードを eval したり、アニメーションのタイマーを走らせたり、という初期化処理をまとめてする p.init という関数が定義されています。これは、 Processing 関数の中で呼ばれます。
p.init = function init(code){ // (略) if ( code ) { // ここのコードで p が eval され、 parse 関数で変換されたコードが eval される。 (function(Processing){with (p){ eval(parse(code, p)); }})(p); } // Processing のコード中に setup 関数を宣言した場合はその関数が実行される if ( p.setup ) { inSetup = true; p.setup(); } // (略) // Processing のコード中に draw 関数を宣言した場合は p.loop が実行される // p.loop は内部で setInterval を呼び出し、そこから、定期的に draw 関数が呼び出される。 if ( p.draw ) { if ( !doLoop ) { p.redraw(); } else { p.loop(); } } // イベントの初期化のコード };
1039 - 1058 行目: p.loop
アニメーションの開始を行う p.loop も見ておきましょう。
p.loop = function loop() { if ( loopStarted ) return; var looping = setInterval(function() { try { // これを curFrameRate で指定された間隔で呼び出してるだけ。 // curFrameRate は p.frameRate 関数で設定することができる。 p.redraw(); } catch(e) { clearInterval( looping ); throw e; } }, 1000 / curFrameRate ); loopStarted = true; }
こんな感じで、シンプルに redraw を読んでるだけですね。
1025 - 1037 行目: p.redraw
p.redraw も見ておきましょう。
p.redraw = function redraw() { if ( hasBackground ) { p.background(); } inDraw = true; p.pushMatrix(); p.draw(); p.popMatrix(); inDraw = false; }
おお。 background は毎回書いてくれるんですね。。。。と思ったけど、どこにも hasBackground フラグを建てる方法が無かったです><(毎回、自分で background を呼ばなきゃダメみたいですね。)
p.pushMatrix と p.popMatrix では、現在の塗りの色とかの描画状態を保存して、戻してます。
まとめ
そろそろまとめますよ。と。