2011/02/09

魔法使いの弟子(その5-1)

【ミッション: 動きをつけて写真を魅せたい】

つくば大学のらいんくんは、どうやら Smooth Div Scroll のサンプルがいたく気に入ったらしく、JavaScript (というかjQuery)に興味を持ったようだ。そうだよね、らいんくんもそろそろそういうお年頃だよね。

ただ、jQuery の知識は必ずしも必要ではなく、 Java + α の知識だけでも割と簡単にこんなものが作れる。



いきなりだが、ソースコードをお見せしよう。中身は特に意識する必要はない。

【program.pjs】
ArrayList imgList;
ArrayList picList;

float bound;

void setup() {
  size(400, 400);
  frameRate(30); // これがないと chrome で速度がおかしくなる
  bound = 100;
  imgList = new ArrayList();
  imgList.add(certainLoadImage("Autumn Leaves.jpg"));
  imgList.add(certainLoadImage("Creek.jpg"));
  imgList.add(certainLoadImage("Desert Landscape.jpg"));
  imgList.add(certainLoadImage("Dock.jpg"));
  imgList.add(certainLoadImage("Forest.jpg"));
  
  picList = new ArrayList();
  for(int i = 0; i < imgList.size(); i++) {
    picList.add(new Picture((PImage)imgList.get(i), 
      random(width-2*bound)+bound, 
      random(height-2*bound)+bound));
  }
}

void draw() {
  background(10, 10, 10);
  int index = -999;
  for(int i = 0; i < picList.size(); i++) {
    if (((Picture)picList.get(i)).update()) {
      if (index < i) index = i; 
    }
  }
  
  Picture p;
  if (index >= 0 && index < picList.size()-1) {
    p = (Picture)picList.get(index);
    picList.remove(index);
    p.dx = random(10)-5;
    p.dy = random(10)-5;
    p.da = radians(random(4)-2);
    picList.add(p);

  }
}

class Picture {
  PImage img;
  float angle;
  float x, y;
  float w, h;
  
  float da, dx, dy;
  
  Picture(PImage img, float x, float y) {
    // 中心が基準
    angle = radians(random(90) -45);
    this.img = img;
    this.x = x;
    this.y = y;
    
    // Processing.js では PImage の初期化のタイミングがおかしく、
    // コンストラクタ内で width と height を取得できない。
    // そのため、w(幅)と h(高さ)を別に持って、
    // そこへサイズを与えておく事にする。
    w = 160;
    h = 120;
    
    da = radians(random(10)-5)*0.001;
    dx = radians(random(10)-5)*0.01;
    dy = radians(random(10)-5)*0.01;
    
  }
  boolean isGrabbing;
  int offsetX;
  int offsetY;
  
  boolean update() {

    float tmpX = mouseX - x;
    float tmpY = mouseY - y;
    float mX = cos(angle)*tmpX +sin(angle)*tmpY + x;
    float mY = cos(angle)*tmpY - sin(angle)*tmpX + y;
    
    boolean isFocussing = false;
    // オンマウス判定
    if((pmouseX != mouseX) || (pmouseY != mouseY)) {
      if(mX > x-w/2 && mX < x+w/2 && mY > y-h/2 && mY < y+h/2) {
        isFocussing = true;
      }
    }

    pushMatrix();
    translate(x, y);
    rotate(angle);
    translate(-w/2, -h/2);
    strokeWeight(10);
    stroke(230, 200);
    fill(255);
    rect(0, 0, w, h);    
    image(img, 0, 0);
    popMatrix();
    
    
    if(x + dx < bound || x + dx > width-bound) dx = -dx;
    if(y + dy < bound || y + dy > height-bound) dy = -dy;
    x += dx;
    y += dy;
    angle += da;
    
    dx *= 0.9;
    dy *= 0.9;
    da *= 0.9;
    return isFocussing;
  }
}

// 確実な画像読み込み
PImage certainLoadImage(String name) {
  PImage img = null;
  while(img == null) {
    img = loadImage(name);
  }
  return img;
}
どこかで見覚えがあると思ったらコレだ。

実は、『魔法使いの弟子』で公開しているプログラムの殆どは Processing という Java の派生言語で作られている。Processing は、ビジュアル面に特化している事と、無駄にオブジェクト指向っぽくない事、そして Java ゆずりの高い移植性によって、ノンプログラマでもとっつきやすく、メディア・アートへの応用がしやすいという素晴らしい利点を持っている。

Processing で作られたプログラムは、通常 Java アプレットの形でエクスポートされるが、John Resig 謹製の Processing.js を使う事で、Java アプレットではなく HTML5 の canvas 内で実行されるようになるのだ。



Processing.js の使い方に関する非常にやる気のない解説というか備忘録。

  1. まずはじめに、適当なディレクトリを作る。以後、このディレクトリ内で作業を行う。
  2. 次に、Processing.js の公式サイトから最新版の processing.js をダウンロードして解凍する。2011年2月9日現在の最新版は processing-1.0.0.js であった。他にも色々ごちゃごちゃとファイルが入っているが、とりあえず processing-1.0.0.js だけを先ほどのディレクトリに放り込む。
  3. 上記のような Processing のソースファイルを、適当な名前(たとえば program.pjs)で保存する。※画像が無いので、そのままコピペしただけでは動かないよ。
  4. プログラム内で読み込む画像ファイルなども、同じディレクトリに放り込んでおく(Processing の場合は data ディレクトリを作り、その下にリソースを入れていたが、ここではソースファイルと同階層にしないとうまく動かない)。
  5. 最後に、2つのファイルを作る。一つは JavaScript、もう一つは HTML ファイルである。
【init.js】
window.onload = function()
{
  var canvas = document.getElementsByTagName("canvas");
  for ( var i = 0; i < canvas.length; i++ )
  {
    Processing( canvas[i], canvas[i].previousSibling.textContent );
  }
};

【hoge.html】
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
 <head>
  <title></title>
  <script type="text/javascript" src="processing-1.0.0.js"></script>
  <script type="text/javascript" src="init.js"></script>
 </head>

 <body>
   <canvas datasrc="program.pjs" width="400" height="400"></canvas>
 </body>
<html>

init.js の方はたぶんこのままコピペで構わない。hoge.html は、読み込むソースファイルとその幅・高さを適宜調整する必要があるだろう。

以上をまとめて Web サーバに転送する。転送先のディレクトリには、以下のファイルが揃うはずである。
  • ./
    • hoge.html (HTMLファイル)
    • program.pjs (Processing のソース)
    • xxxx.jpg (表示する画像ファイル)
    • xxxx.jpg (   〃   )
    • (中略)
    • init.js (さっき作ったやつ)
    • processing-1.0.0.js (落としてきたやつ)
とりあえず、この時点の環境を再現できるように、一連のファイルを zip にまとめて置いておく事にする。ダウンロードして解凍したら、hoge.html を IE 以外のブラウザで開いてみてほしい。

以上でめでたく視覚効果をふんだんに盛り込んだ写真ページが作れた。と見せかけて、IE が canvas タグに対応していないため、環境によっては見られない場合がある。巨大なシェアを誇る有力ブラウザが HTML5 のサポートに消極的というのは致命的な話だ。

仕方ないので、IE でこのブログを閲覧した方に対しては、誠に不本意ながら Java アプレットバージョンが表示されるようにしている。ちなみに、IE が canvas に対応するのは Ver. 9 以降らしい。



おまけ。

自作の Web サイトなら上記の方法だけで充分なのだが、アップロード制限が厳しい Blogger (このブログ)に埋め込むには少し工夫が必要だった。

引っかかった中でも特に大きな問題が、 processing.js の仕様上、画像を絶対パスで読み込めないという事。

ぼくが書いたブログの記事はトップページだけでなく、個別ページやアーカイブにも表示される。そしてそれらのページが(サーバー上の)同じ階層のディレクトリ内に生成されるとは限らないため、アップロードした画像には絶対パスでアクセスする事になる。もちろん、外部サーバに画像ファイルを置いた場合は言うまでもなく絶対パスでアクセスしなければならない。

しかし、processing.js を用いて表示できる画像は相対パス指定のみ。色々考えた末、外部の HTML ファイルをインラインフレームで読み込む方法に落ち着いた。

iframe タグは廃止される傾向にあり、現在は object タグの使用が推奨されているらしい。ただし、IE からのアクセスに限っては直貼りしたアプレットをそのまま表示する事にした。ブログに書いた HTML ソースは以下の通り。

<!--[if IE ]>
   <applet archive="http://www.tercel-tech.com/blog/album/album.jar" code="album.class", width="400", height="400"></applet>
<![endif]-->
<![if !IE ]>
      <object type="text/html" data="http://www.tercel-tech.com/blog/album/index.html" style="width:400px;height:400px;overflow:hidden;hspace:0,vspace:0;margin:0;border:0;"></object>  
<![endif]>

これで一応の解決を見たのだが、やはり複数のサーバに管理対象が分散するというのはけっこう面倒な話である。

自分で自在にカスタムできる Web サイトならば、(IEへの対応はさておき)ここまでまどろっこしい真似をしなくても済むので、ぜひ試してみる事をおすすめする。