Express で JSONP をスマートにレスポンスする方法

Express にて JSONP をレスポンスする時、今まではこんなふうにやっていました。

app.get('/awesome/your/api', function(req, res) {
  var response = { sugar: "sweet", salt: "spicy" }
    , callback = req.query.callback;

  if (callback) {
    // コールバックが指定されていたら JSONP をレスポンス
    res.send(callback + '(' + JSON.stringify(response) + ');');
  } else {
    // コールバックが指定されていなければ JSON をレスポンス
    res.send(response);
  }
});

マニュアルにはありませんが、実はフレームワーク側で JSONP のレスポンスがサポートされています。

// res.send() にて JSONP を有効にするオプション
// GET パラメータに callback の指定があると JSONP でレスポンス
// app.config に書いておくと良さげです。
app.enable('jsonp callback');

app.get('/your/awesome/api', function(req, res) {
  var response = { sugar: "sweet", salt: "spicy" };
  // 設定が有効になっているのでフレームワーク側で自動で判別してくれる
  res.send(response);
});

/your/awesome/api

{"sugar":"sweet","salt":"spicy"}

/your/awesome/api?callback=fn

fn({"sugar":"sweet","salt":"spicy"});

フレームワークの機能を使うと、余計な if 文を書かなくてもいい上に、危険な文字もエスケープしてくれるのでこちらを使ったほうが良いでしょう。なお、コールバック関数名を指定するパラメータ名は変更できないみたいです。

以上、ちょっとした小ネタでした。

draw3DLine : 3D drawing on 2D canvas (Processing.js)

最近、3Dになったユニバ社のロゴ
3Dの描画機能だけを切り出してみたので、皆さん使ってね。

ユニバの新ロゴはcanvas+Processing.jsで作られていますが、Processingの3D系の命令は使っていません(WebGLに対応しているブラウザがまだ少ないので…)。3Dのオブジェクトをスクリーン(canvas)に投影する計算を行って、2D系の命令だけで描画しています。
その描画系を切り出したのが以下の関数です。

使い方

draw3DLine(x1, y1, z1, x2, y2, z2, xRotate, yRotate, zRotate)

6つの引数(XYZ座標を2つと、XYZ軸の回転角度)を渡すと、canvasに2座標を結ぶ線を描画します。原点は画面の中央です。
カメラはZ軸上に固定です。カメラの視野角(radS)、カメラの位置(camZ)、スクリーン位置(scZ)だけ、変更することができます。線の太さ、色はweight、strokeH、strokeS、strokeBで持っていますが、2Dで線を描画するパラメータなので、太い線ではパース感の矛盾がよりはっきり見えてきます。細い線でも厳密に言えば奥行き感ゼロ。そこは見せ方でがんばってね。

サンプルでは、線を12本引き、XYZ軸それぞれを1度ずつ回転させて、直方体が回転するアニメーションを作っています。
http://hascanvas.com/draw3DLine

//透視変換用の変数
//視野角θ
float radS = TWO_PI/360*35;
//カメラの原点からの距離
float camZ = -4500;
//スクリーンの原点からの距離
float scZ = -1000;
//回転
float xRotate = 0;
float yRotate = 0;
float zRotate = 0;

//描画用の変数
//線の太さ
float weight = 0.5;
//線の色
int strokeH = 0;
int strokeS = 0;
int strokeB = 0;

//頂点座標を格納する配列 : x1, y1, z1, x2, y2, z2
int[][] va = {
  {-200,-200,-20, 200,-200,-20},
  { 200,-200,-20, 200, 200,-20},
  { 200, 200,-20,-200, 200,-20},
  {-200, 200,-20,-200,-200,-20},
  {-200,-200, 20, 200,-200, 20},
  { 200,-200, 20, 200, 200, 20},
  { 200, 200, 20,-200, 200, 20},
  {-200, 200, 20,-200,-200, 20},
  {-200,-200,-20,-200,-200, 20},
  { 200,-200,-20, 200,-200, 20},
  { 200, 200,-20, 200, 200, 20},
  {-200, 200,-20,-200, 200, 20}
};

//初期化
void setup()
{
  size(300, 300);
  frameRate(30);
  colorMode(HSB);
  noSmooth();
  noFill();
  background(255);
}

//メインループ
void draw()
{
  background(255);
  //描画ループ
  for(int i=0; i<va.length; i++) {
    //描画
    //x1, y1, z1, x2, y2, z2, rotateX, rotateY, rotateZ
    draw3DLine(va[i][0],va[i][1],va[i][2],va[i][3],va[i][4],va[i][5], xRotate, yRotate, zRotate);
  }
  //回転
  xRotate += TWO_PI/360;
  yRotate += TWO_PI/360;
  zRotate += TWO_PI/360;
}

//透視変換 x1, y1, z1, x2, y2, z2, rotateX, rotateY, rotateZ
void draw3DLine(float x1,float y1, float z1, float x2, float y2, float z2, float rx, float ry, float rz)
{
  //Y軸の回転量を反映
  float z1cash = z1;
  float z2cash = z2;
  z1 = x1 * sin(ry) - z1cash * cos(ry);
  z2 = x2 * sin(ry) - z2cash * cos(ry);
  x1 = x1 * cos(ry) + z1cash * sin(ry);
  x2 = x2 * cos(ry) + z2cash * sin(ry);
  //X軸の回転量を反映
  z1cash = z1;
  z2cash = z2;
  z1 = y1 * sin(rx) - z1cash * cos(rx);
  z2 = y2 * sin(rx) - z2cash * cos(rx);
  y1 = y1 * cos(rx) + z1cash * sin(rx);
  y2 = y2 * cos(rx) + z2cash * sin(rx);
  //Z軸の回転量を反映
  float x1cash = x1;
  float x2cash = x2;
  float y1cash = y1;
  float y2cash = y2;
  x1 = x1 * sin(rz) + y1cash * cos(rz);
  x2 = x2 * sin(rz) + y2cash * cos(rz);
  y1 = y1 * sin(rz) - x1cash * cos(rz);
  y2 = y2 * sin(rz) - x2cash * cos(rz);
  //透視変換
  float x1b = width/2  + x1 * (camZ + z1) * tan(radS/2) / (scZ + z1) * tan(radS/2);
  float y1b = height/2 + y1 * (camZ + z1) * tan(radS/2) / (scZ + z1) * tan(radS/2);
  float x2b = width/2  + x2 * (camZ + z2) * tan(radS/2) / (scZ + z2) * tan(radS/2);
  float y2b = height/2 + y2 * (camZ + z2) * tan(radS/2) / (scZ + z2) * tan(radS/2);
  //描画開始位置を初期化
  translate(0,0);
  //線の色・太さ
  stroke(strokeH, strokeS, strokeB);
  strokeWeight(weight);
  //描画
  line(x1b,y1b,x2b,y2b);
}

jQueryでグラフを書く

集計データをグラフ化したいなー、ということでどういう道具が使えるの探してみました。
以前、河合さんがFlashで描くライブラリについて取り上げていたのだけれど、iOS系で表示できないとやだね、ということで、Flashじゃないものを探してみました。

で、まずは、Google Chart API
Googleさんが、Web baseのAPIとして提供しているもの。Analyticsの中でも使われているとか。
GETのQUERY STRINGで値を渡すらしい。描画はサーバサイドの画像処理ですね。
データが外に漏れるのを嫌うシチュエーションでは使いにくそうです。

JavaScript系はどんなかなというところで、出てきたのがflot
こちらは、jQuery系プラグインで、シンプルにまとまっていてよさそう。Canvasに描画するんですね。
使うのも比較的簡単。divのプレースホルダをもってきて、$.plot() というメソッドに、データと
オプションの配列と一緒に渡す。シンプル。

コードはこんな感じ

$(document).ready(function() {
	var data_results_cumulative = [[1301270400000,0],[1301875200000,8.25],[1302480000000,22],[1303084800000,29],[1303689600000,36.5]];
	var data_plan_cumulative = [[1301270400000,12],[1301875200000,24],[1302480000000,36],[1303084800000,48],[1303689600000,60]];
	var data_firstplan_cumulative = [[1303689600000,16]];
	var ticks = [1301270400000,1301875200000,1302480000000,1303084800000,1303689600000];

	$.plot($("#graph"), [
		{ label: "results", data: data_results_cumulative, color: 2},
		{ label: "plan", data: data_plan_cumulative, color: 0},
		{ label: "first plan", data: data_firstplan_cumulative, color: 1},
	],
	{
		series: {
			lines: { show: true },
			points: { show: true }
		},
		legend: {
			position: "nw"
		},
		xaxis: {
			mode: "time",
			timeformat: "%y-%m-%d",
			tickSize: [7, "day"],
			ticks: ticks
		},
		yaxis: {
			ticks: 10,
			min: 0,
		},
		grid: {
			backgroundColor: { colors: ["#fff", "#eee"] },
		}
	});

});

Titanium Mobile 勉強会に参加してきました

どうもこんにちは!みなさま iPhone アプリしてますか?!僕は未だにガラケーユーザです (涙) iPhone 5 が出たら本気をだそうかと思ってます。

そんなわけで 2/25 に行われた株式会社コンテンツワンさん主催の勉強会 Titanium Mobileで始めるiPhone / Androidアプリ開発 に参加してきました。

下書きのまま放置してしまい今更感もあるのですが、会場で取ったメモにちょっと加筆したものを公開します。


  • 講師 masuidrive (blog)
  • Titanium Mobie の実績
    • WikiWalker (iPhone)
      • 位置情報からゆかりの地を Wikipedia から検索してくれる
    • なごや乗換ブラウザ (iPhone)
      • 乗り換え案内アプリ
    • Android 映画情報「myシアター」 (Android)
      • もともとは iPhone 向けに Objective-C で書かれた
      • Android 版を製作するに当たって Titanium Mobile で書かれる (3 週間で完成)
    • TOKINOWA
      • ちょっとおしゃれな時計アプリ
    • はてなカウンティング
      • はてな謹製
    • MogSnap: 食べ物の写真を投稿
      • 食べ物の写真を撮影して投稿
      • ソーシャルゲームっぽい
      • 相手に食べたいなあと同調してもらえるとポイントアップ
      • アニメーションがリッチ
    • AKB前田敦子 Maeda-1 グランプリ (Android)
    • Follow me now (iPhoneAndroid)
      • 位置情報を使ってアプリを起動しているユーザ同士をフォローできる
      • masuidrive 謹製
      • 本当は今日のデモ向けにつくっていたが作り込みしていって話しきれない量になってしまった
      • 近々オープンソース化する予定
  • Titanium Developer
    • JavaScript で iPhone/Android クロス開発
    • IDE じゃないよ
    • Aptana 社買収して Full feature IDE を 3 月後半リリース予定
    • 今は printf デバッグぐらいしかできない。今後 IDE にデバッガが搭載される予定 (時期未定)
    • 原則 iPhone SDK の Beta 版には対応しない
    • SDK 複数バージョン入れてる場合は xcode-select で最新版を選んで
    • Android SDK Tools R8 以降では adb をコピー
    • C/Java で書かれたモジュールをロードする場合 manifest, tiapp.xml を編集
  • JavaScript を Objective-C/Java にトランスレートしているわけではなくインタプリタで実行している
    • OS の機能を JavaScript から簡単に呼び出せるところが Titanium のコアとなる技術
    • iPhone 版のインタプリタは WebKit 由来の JavaScriptCore (規約避けの意味もあった)
    • Android のインタプリタは Rhino を搭載
  • Titanium SDK 1.6
    • つい先日出た
    • 1.5 以前は iPhone に比べると Android のサポートはいまいちだった
    • Titanium SDK 1.6 から Android サポートがかなり良くなった、おすすめ
  • MogSnap の事例
    • 99% は JavaScript のコード
    • 残りは JPEG の圧縮率を変える処理 (Objctive-C で 10 行ぐらいのコード)
    • アニメーション・エフェクトは 2D Transform, 3D Transform が使える
    • Ti.UI.createAnimation(); / Ti.UI.create2DMatrix();
    • Ti.UI.createView() div タグみたいなやつ
  • Titanium Mobile の弱点
    • サンプル不足
      • リファレンスは比較的充実しているがチュートリアルがあまり無い
      • デモアプリの KitchenSink のコードが参考になる
    • リアルタイム性の高いものは厳しい
      • ゲームなどで大量のパーティクル飛ばすとか将棋の思考ルーチンとか
    • 画像処理 API が無かったり
      • ない部分、苦手な部分は Java/Objective-C で Module として書けば OK
      • masuidrive 氏が以前作ったコミックビュアーは通信部分などは JavaScript, UI は Objective-C で書いた
      • モジュールとして作れば JavaScript から Objective-C を呼び出したりその逆も出来る
  • 開発スタイル
    • 1 Window = 1 Source: Window 間の連携は苦手。複数人開発だと有利。変数の共有は Ti.App で。
      • Ti.UI.createWindow({ title: “..”, url: “hoge.js” }); で hoge.js に書いた Window のコードを呼び出し
    • 1 Project = 1 Source: 変数の共有や Window 間の連携が得意。複数人開発には向かない。
    • 併用も出来るっぽい?
  • 通信系
    • XHR のクローンとして Ti.Network.HTTPClient というものが用意されている
    • YQL 標準搭載 (XML -> JSON の変換も容易)
    • 余談: Echofon とかの引っ張ってリロードするやつは Pull to Refresh というらしい
  • Android と iPhone ではアイコンのサイズが違う
    • それぞれ専用で作ってあげると良い
    • フリーの Android アイコン配布サイト Android Icons
  • ローカルストレージ
    • SQL Database (SQLIte)
    • ファイルにも保存できる
  • コーティングインジケータを通信していない時でも回したい → モジュールを書く
  • RouteMe オフラインで地図見れる
  • AirPrint 対応したい → Titanium でサポートしていない → モジュールを書く
  • 足りない機能はモジュールを書く
  • Modern JavaScript
    • Titanium Mobile は JavaScript 1.6 が使える
    • CommonJS
      • hoge.js: exports.foo = function() { … }
      • var hoge = require(“hoge”); hoge.foo();
      • createHogehoge = function() { if (android) return new createAndroidHogehoge(); else if (iphone) return new …
    • Deferred, Promise, Emitter
      • callback でもネストが深くならない
      • でぃふぁーれっど (でぃふぁーどじゃないらしい)
      • JSDeferred は Titanium でも使えるらしい
  • ドキュメントやリソースなど
    • http://code.google.com/p/titanium-mobile-doc-ja/
      • API ドキュメントの日本語の翻訳
      • 何故か本家のコードの間違いが翻訳では直っていることがある
      • 何故か本家よりスクリーンショットが充実していることもある
    • http://tidocs.com/
    • WebDB Press Vol. 61 の特集
    • ぐぐる
      • 情報の賞味期限は 4〜6ヶ月
      • それより古い情報は食べたらおかなをこわします (笑)
  • Q. jQuery の便利な機能を使うことはできるか。jQuery.each(), 等の DOM に依存しないものとか。
    • A. Titanium の JavaScript は 1.6 相当なので、Array.prototype.filter (jQuery.grep に相当), Array.prototype.map (jQuery.map に相当) など、標準化されているものを使えば OK
  • Q. 描画系でリアルタイム処理するときはどうする?
    • A. Objective-C で UIView を継承したクラスに描画してモジュールにして Titanium から呼び出せば良い
    • コミックビュアーを作ったときはそうした

懇親会のかつサンド美味かった!というわけで以上!

ページをゆるふわっと表示する jQuery Chaining Effects Plugin (jquery.effectchain.js)

はいこんにちは!今回はこことかここみたいなサイトのページ表示エフェクトを簡単に実現するプラグインをつくってみたのでそのご紹介です!

Internet Explorer 6+, Safari 5, Google Chrome 8, Firefox 3.6, Opera 11 で動作確認済み。不具合や動作報告などありましたらコメント欄でご連絡ドーゾ。


動作デモ

画像ではお伝えしにくいので、映像で。最初の 20 秒までがプラグインのデモになります。


特徴

簡単な記述でリッチなページ表示演出ができます。また、カスタムデータ属性を使って要素ごとにアニメーションを変えたりインターバル・ディレイを指定して表示のタイミングを制御することも可能です。(すごい!)


ダウンロード

ソースコードは GPL v2 と MIT ライセンスのデュアルライセンスです。


使い方

一般的なプラグインと同様 jQuery と一緒にプラグインファイルを script タグで読み込みます。js ファイルで完結しているので追加の CSS などはロードする必要はありません。(あんしん!)

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript" src="jquery.effectchain.js"></script>

jQuery は 1.4.3 以降を使ってください。こちらでは 1.4.3, 1.4.4, 1.5 で動作確認済みです。

一番シンプルな使い方

スクリプトをロードした状態で以下の記述を行うと、ページ内の div 要素がマークアップした順番でぱらぱらと表示されます。(かんたん!)

jQuery(function($) {
    $("body").effectChain({ target: "div" });
});

引数を指定して動作のカスタマイズ

引数を取ることもできます。(カスタマイザボー!)

jQuery(function($) {
    // アニメーション対象要素を包含する親となる要素を指定します
    // 親要素自身はアニメーション対象とはなりません
    $("#container").effectChain({
        // アニメーション対象の要素を指定します
        // jQuery Selector が使えます
        target: ".effectChain",
        // アニメーションが始まるまでの時間をミリ秒で指定します
        delay: 0,
        // アニメーション間隔をミリ秒で指定します
        interval: 50,
        // アニメーションの順序を指定します
        // true: DOM の逆順, false: DOM の正順
        reverse: false,
        // 各要素のアニメーション開始前の初期スタイルを指定します
        css: {
            opacity: 0
        },
        // animate 関数の引数を配列で指定します
        // 配列の要素がそれぞれ第一引数、第二引数に相当
        args: [{
            opacity: 1
        }, {
            duration: 500,
            easing: "swing"
        }]
    });
});

jQuery.animate() メソッドについてはこちらが詳しいです。

要素ごとのカスタマイズ

アニメーション対象の各要素にカスタムデータ属性を指定することで要素ごとのパラメータを指定することができます。カスタムデータ属性に対応するパラメータは以下のとおりです。(グレート!)

  • data-fxchain
    • effectChain メソッドの引数にパラメータを上書きします
  • data-fxchain-params
    • animate メソッドの第一引数のパラメータを上書きします
  • data-fxchain-options
    • animate メソッドの第二引数のパラメータを上書きします

カスタムデータ属性はシングルクォーテーションで囲い、データは JSON フォーマットで記述します。以下のサンプルを参考にしてみてください。

<div id="#container">
    <div class="effectChain"
        data-fxchain='{ "css": { "fontSize": "10px" } }'
        data-fxchain-params='{ "fontSize": "18px" }'>
        フォントが 10px から 18px にかけて大きくなる
    </div>
    <div class="effectChain"
        data-fxchain='{ "interval": 0 }'>
        次の要素のアニメーションが始まるまでの間隔を 0 にする<br />
        ↓ と同時に登場
    </div>
    <div class="effectChain"
        data-fxchain='{ "interval": 0 }'>
        ↑ と ↓ と同時に登場
    </div>
    <div class="effectChain"
        data-fxchain='{ "interval": 0 }'>
        ↑ と同時に登場
    </div>
    <div class="effectChain"
        data-fxchain='{ "delay": 500 }'
        data-fxchain-options='{ "duration": 1000 }'>
        500 ミリ秒待ってからアニメーション開始
    </div>
</div>

具体的な実装はデモサイトのソースコードを参考にしてみてください!(なげやり)


というわけでいかがでしょうか?! このプラグインを使ってふるふわ愛され上手を目指して頑張りましょう!!!(?)

Node.js 用パッケージマネージャ npm と Sinatra ライク軽量フレームワークの express を使ってみた

おばんです。昨日は node.js のセットアップで終わってしまいましたが、今日は Hello, world までやってみます。


謝辞

今回エントリするにあたって以下の記事を参考にさせていただきました。ありがとうございます。


npm のセットアップ

Node.js には rpm によく似たパッケージ管理を実現する npm というものが存在します。他に同様のもので kiwi というものも存在するのですが、今後は npm を使うことが推薦されているようです。以下 kiwi の公式サイトより引用。

NOTE: kiwi is no longer supported, please use npm. This repository will remain for educational purposes only

まずは npm がないとライブラリの導入もままならないのでまっさきにインストールしてみます。セットアップに先立って、ホームディレクトリに設定ファイルを記述します。

$ vim ~/.npmrc
root = ~/.npm/libraries
binroot = ~/.npm/bin
manroot = ~/.npm/man

設定ファイルを記述したら次は環境変数を設定します。bash の場合は .bashrc に以下の内容を記述しておけばログイン時に自動的に設定されるようになります。

export NODE_PATH=~/.npm/libraries:$NODE_PATH
export PATH=~/.npm/bin:$PATH
export MANPATH=~/.npm/man:$MANPATH

source コマンドを使って今しがた設定した環境変数をロードします。(もしくは再ログインでも OK)

$ source .bashrc

設定が終わったら以下のコマンドで npm 本体をインストールします。

$ curl http://npmjs.org/install.sh | sh
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100   661  100   661    0     0    594      0  0:00:01  0:00:01 --:--:--     0
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100  2531  100  2531    0     0   1889      0  0:00:01  0:00:01 --:--:--  9271
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100  576k  100  576k    0     0   188k      0  0:00:03  0:00:03 --:--:--  226k
node cli.js cache clean
npm info it worked if it ends with ok
npm info using npm@0.2.4-1
npm ok
node cli.js rm npm
npm info it worked if it ends with ok
npm info using npm@0.2.4-1
npm info not installed npm
npm ok
node cli.js install npm
npm info it worked if it ends with ok
npm info using npm@0.2.4-1
npm info fetch http://registry.npmjs.org/npm/-/npm-0.2.4-1.tgz
npm info install npm@0.2.4-1
npm info preinstall npm@0.2.4-1
npm WARN bins installing to /home/seiya/.npm/bin, outside PATH
npm WARN npm@0.2.4-1 linkMans It seems /home/seiya/.npm/man might not be visible to man
npm WARN npm@0.2.4-1 linkMans For greater justice, please add it to your man path
npm WARN npm@0.2.4-1 linkMans See: `man man`
npm info install npm@0.2.4-1
npm info postinstall npm@0.2.4-1
npm info activate npm@0.2.4-1
npm info preactivate npm@0.2.4-1
npm info activate npm@0.2.4-1
npm info postactivate npm@0.2.4-1
npm info build Success: npm@0.2.4-1
npm ok
It worked

正常にインストールが出来ていれば npm コマンドが ~/.npm/bin/ 下に生成されます。

$ npm --version
0.2.4-1

ぐれーと。


express をセットアップする

Node.js には HTTP サーバを実現するライブラリがバンドルされているのですが、プリミティブなものなのでここではフレームワークを使ってみることにします。今回は Sinatra ライクに簡潔にルーティングが出来る express というフレームワークを使ってみます。早速 npm を使ってインストールしてみましょう。

$ npm install express
npm info it worked if it ends with ok
npm info using npm@0.2.4-1
npm info fetch http://registry.npmjs.org/express/-/express@1.0.0rc4.tgz
npm info fetch http://registry.npmjs.org/connect/-/connect-0.2.7.tgz
npm info install express@1.0.0rc4
npm info install connect@0.2.7
npm info preinstall express@1.0.0rc4
npm info preinstall connect@0.2.7
npm WARN bins installing to /home/seiya/.npm/bin, outside PATH
npm info install express@1.0.0rc4
npm info postinstall express@1.0.0rc4
npm info activate express@1.0.0rc4
npm info preactivate express@1.0.0rc4
npm info activate express@1.0.0rc4
npm info postactivate express@1.0.0rc4
npm info install connect@0.2.7
npm info postinstall connect@0.2.7
npm info activate connect@0.2.7
npm info preactivate connect@0.2.7
npm info activate connect@0.2.7
npm info postactivate connect@0.2.7
npm info build Success: express@1.0.0rc4
npm info build Success: connect@0.2.7
npm ok

つつがなくインストールが終わってしまいました。公式サイトのサンプルに則って適当にコードを書いてみます。ファイル名は server.js とします。

var app = require("express").createServer();

app.get("/", function(req, res) {
	res.send("Hello, world!");
	console.log([req, res]);
});

app.listen(8080);

node コマンドで実行してみます。

$ node server.js

これだけでコマンドが中断されるまで listen し続けるサーバの出来上がりです。早速ブラウザから 8080 ポートにアクセスしてみるとまばゆいばかりの Hello, world! をお目にかかることが出来ました。console.log() の内容は node の標準出力に表示されるようです。ちょっと長いですが req, res の内容を貼りつけてみました。

$ node server.js
[ { socket:
     { fd: 7
     , type: 'tcp4'
     , secure: false
     , _readWatcher: [Object]
     , readable: true
     , _writeQueue: []
     , _writeQueueEncoding: []
     , _writeQueueFD: []
     , _writeWatcher: [Object]
     , writable: true
     , _writeImpl: [Function]
     , _readImpl: [Function]
     , _shutdownImpl: [Function]
     , remoteAddress: '110.4.189.82'
     , remotePort: 55368
     , server: [Object]
     , _outgoing: []
     , __destroyOnDrain: false
     , ondrain: [Function]
     , _idleTimeout: 120000
     , _idleNext: [Object]
     , _idlePrev: [Circular]
     , _idleStart: Sat, 23 Oct 2010 16:38:26 GMT
     , _events: [Object]
     , ondata: [Function]
     , onend: [Function]
     , _onOutgoingSent: [Function]
     }
  , connection: [Circular]
  , httpVersion: '1.1'
  , headers:
     { host: 'rd-sandbox-2.aws.uniba.jp:8080'
     , connection: 'keep-alive'
     , 'cache-control': 'max-age=0'
     , accept: 'application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'
     , 'user-agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.41 Safari/534.7'
     , 'accept-encoding': 'gzip,deflate,sdch'
     , 'accept-language': 'ja,en-US;q=0.8,en;q=0.6'
     , 'accept-charset': 'Shift_JIS,utf-8;q=0.7,*;q=0.3'
     , cookie: '__utmz=258608462.1286871711.6.2.utmcsr=rd.uniba.jp|utmccn=(referral)|utmcmd=referral|utmcct=/blog/; __utma=258608462.1333207560.1282819752.1287049453.1287724320.9'
     }
  , url: '/'
  , method: 'GET'
  , statusCode: null
  , client: [Circular]
  , httpVersionMajor: 1
  , httpVersionMinor: 1
  , upgrade: false
  , originalUrl: '/'
  , query: {}
  , app: [Circular]
  , res:
     { socket: [Circular]
     , connection: [Circular]
     , output: []
     , outputEncodings: []
     , _last: false
     , chunkedEncoding: false
     , shouldKeepAlive: true
     , useChunkedEncodingByDefault: true
     , _hasBody: true
     , finished: true
     , writeHead: [Function]
     , headers: [Object]
     , app: [Circular]
     , req: [Circular]
     , _header: 'HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 13\r\nDate: Sat, 23 Oct 2010 16:38:26 GMT\r\nX-Response-Time: 0ms\r\nX-Powered-By: Connect 0.2.7\r\nServer: Node v0.2.3\r\nConnection: keep-alive\r\n\r\n'
     , _headerSent: true
     }
  , next: [Function: next]
  , params: []
  }
, [Circular]
]

うーん素晴らしい!あとは O/R Mapper があれば簡単な Web アプリケーションが作れそうですね。今回はチュートリアルみたいな感じになってしまいましたが、どんどん高度なことにチャレンジしていきたいですね。それではまた!

Fedora 8 上で node.js を RPM からインストールする

おばんです。最近では JavaScript に夢中になっていて、サーバサイドも JavaScript で書きたくなってきた今日この頃みなさまいかがお過ごしでしょうか。

今回はサーバサイドで JavaScript を実行することが出来る node.js (執筆時点で最新版の v0.2.3) を Amazon AWS の Fedora 8 上で RPM を作ってインストールしてみたので記録として残しておきます。未確認ですが他の RedHat 系のディストリビューションでも通用するかと思います。


前もってソースコードのビルドに必要そうなものを入れておく

# yum groupinstall "Development Tools"

コンパイラや基本的なライブラリをまとめてセットアップしてしまいます。


必要なファイルを集める

# cd /usr/src/redhat/SOURCES
# wget http://nodejs.org/dist/node-v0.2.3.tar.gz

公式サイトからソースコードを入手します。

# cd /usr/src/redhat/SPECS
# wget http://gist.github.com/raw/635036/79e971db1dac505201525d9f13218f734b37ff17/nodejs.spec

すでに node.js の spec ファイルを書いている人がいたので、ありがたく利用させていただきました。(いまや spec ファイルも gist で公開されるような時代なのか……。)


RPM の作成・インストール

rpmbuild コマンドを使ってビルドします。

# rpmbuild -bb nodejs.spec
実行中(%prep): /bin/sh -e /var/tmp/rpm-tmp.86351
+ umask 022
+ cd /usr/src/redhat/BUILD
+ LANG=C
+ export LANG
+ unset DISPLAY

... snip ...

/usr/src/redhat/BUILD/node-v0.2.3/wscript:188: error: Could not autodetect OpenSSL support. Make sure OpenSSL development packages are installed. Use configure --without-ssl to disable this message.
エラー: /var/tmp/rpm-tmp.86351 の不正な終了ステータス (%build)

RPM ビルドエラー:
    /var/tmp/rpm-tmp.86351 の不正な終了ステータス (%build)

OpenSSL の devel パッケージがはいっていないのでビルド出来ずエラーしてしまいました。yum でサクっと入れてしまいます。

# yum install openssl-devel

気をとりなおしてもう一度!

# rpmbuild -bb nodejs.spec
実行中(%prep): /bin/sh -e /var/tmp/rpm-tmp.4886
... snip ...

メガバイト級のソースコードなのでビルドに結構時間がかかります。そろそろ終わりそうなところにさしかかってまたエラーしてしまいました。

... snip ...
ファイルの処理中: nodejs-debuginfo-0.2.3-1.fc8
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
伸張ファイルの検査中: /usr/lib/rpm/check-files /var/tmp/nodejs-0.2.3-1.fc8-tV2607
エラー: インストール済み(ただし未伸張)ファイルが見つかりました:
   /usr/lib/node/wafadmin/Build.pyc
   /usr/lib/node/wafadmin/Build.pyo
   /usr/lib/node/wafadmin/Configure.pyc
   /usr/lib/node/wafadmin/Configure.pyo
   /usr/lib/node/wafadmin/Constants.pyc
   /usr/lib/node/wafadmin/Constants.pyo
... snip ...

エラーメッセージを Google で調べてみると spec ファイル内の定義と実際の BUILDS ディレクトリのファイルに差異があると発生するようです。

上記サイトによると以下のオプションをつけて rpmbuild するとエラーしなくなるということがわかりました。(感謝!)

# rpmbuild -bb --define="__check_files %{nil}" nodejs.spec
実行中(%prep): /bin/sh -e /var/tmp/rpm-tmp.26330
... snip ...

またビルドが始まります。前回のビルド時にオブジェクトファイルが生成されているためか程々の時間でビルドが終了しました。

... snip ...
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
書き込み完了: /usr/src/redhat/RPMS/x86_64/nodejs-0.2.3-1.fc8.x86_64.rpm
書き込み完了: /usr/src/redhat/RPMS/x86_64/nodejs-debuginfo-0.2.3-1.fc8.x86_64.rpm
実行中(%clean): /bin/sh -e /var/tmp/rpm-tmp.60752
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd node-v0.2.3
+ rm -rf /var/tmp/nodejs-0.2.3-1.fc8-Pw6173
+ exit 0

これで /usr/src/redhat/RPMS/ 下に RPM をビルドすることに成功しました。あとはいつものとおり rpm コマンドでインストールすれば OK です。

# cd ../RPMS/x86_64
# rpm -ivh nodejs-0.2.3-1.fc8.x86_64.rpm
準備中...                ########################################### [100%]
   1:nodejs                 ########################################### [100%]

コマンドを叩いてみます。

# node --version
v0.2.3

大丈夫そうですね。


途中躓きながらでしたが無事 RPM からインストール出来ました。手始めに PHP では実装の難しいソケットサーバにチャレンジしてみようかなと思います。それではまた!

jQuery の animate() メソッドをアニメーション以外に応用する方法

前回と同じようなネタですが、今度は逆転の発想で jQuery.animate() をアニメーション以外にも応用する方法をご紹介します。時間の経過が伴う処理の場合は今回の方法を使ったほうが直感的です。

謝辞

今回のサンプルは Steven Wittens 氏の canvas 要素に対して jQuery.animate() 関数を利用する方法を参考にさせていただきました。

http://acko.net/blog/abusing-jquery-animate-for-fun-and-profit-and-bacon

上記ページのデモの空中をベーコンが飛び交うさまは一見の価値ありです。(笑)


jQuery.animate() で音声のフェードイン・フェードアウトを実装する

Live Demo (requires Google Chrome, Firefox 3.5 or higher, Safari 3 or higher)

HTML5 から audio 要素と JavaScript を使ってネイティヴで音声を再生・制御する仕組みが用意されました。この機能を使ってオーディオのフェードイン・フェードアウトを jQuery.animate() メソッドを応用して実装してみます。

HTML

<body>
	<div>
		<button id="fadeIn">Fade In</button>
		<button id="fadeOut">Fade Out</button>
	</div>
	<audio>
		<!-- for Safari, Chrome -->
		<source src="sallsamma_ting.mp4" />
		<!-- for Firefox, Opera -->
		<source src="sallsamma_ting.ogg" />
	</audio>
</body>

制御用のボタンと、オーディオタグを記述しておきます。ブラウザによってサポートする音声形式が異なるので MP4 と Vorbis を指定しておきます。

JavaScript

// プロパティ保持用のダミーの div 要素を作成する
// jQuery.exntend() メソッドを使って volume というプロパティを初期値 0 で追加する
var vars = jQuery.extend($('<div>')[0], { volume: 0 });

jQuery(function($) {
	var audio = $("audio:first")[0];	// 要素の参照を取得する
	audio.volume = 0;	// 初期設定 (0 = 無音, 1 = 最大音量)

	// イベントの割り当て
	$("button#fadeIn").live("click", fadeIn);
	$("button#fadeOut").live("click", fadeOut);

	/**
	 * フェードインしながら再生開始
	 */
	function fadeIn(e) {
		if (!audio.paused) {
			return;
		}
		audio.play();
		$(vars).stop().animate({ volume: 1 }, {
			easing: "linear",
			duration: 2000,
			step: function() {
				audio.volume = this.volume;	// this == vars
			}
		});
	}

	/**
	 * フェードアウトしながら再生停止
	 */
	function fadeOut(e) {
		if (audio.paused) {
			return;
		}
		$(vars).stop().animate({ volume: 0 }, {
			easing: "linear",
			duration: 2000,
			step: function() {
				audio.volume = this.volume;	// this == vars
			},
			complete: function() {
				audio.pause();
			}
		});
	}
});

3 行目がキモです。vars というダミーの要素を生成して volume というプロパティを追加します。fadeIn(), fadeOut()varsvolume プロパティを jQuery.animate() で操作することにより、フェードイン・フェードアウトを実現しています。もちろんイージングを取り替えたり duration を変更してフェードの効果を変更することも可能です。

いかがでしたでしょうか。今回は音声のフェードイン・フェードアウトを実装してみましたが、他にも色々応用できると思います。ぜひチャレンジしてみてください!それでは!

JavaScript でロード時にコンテンツの再描画が発生しても初期状態がちら見えしないようにする方法 (HTML5 的には valid だぜ)

どうもこんにちは。

ページをロードする際に JavaScript でコンテンツの再描画が発生するような処理を行う場合、IE など一部のブラウザではスクリプト実行前の状態が一瞬表示されてから再描画される場合があります。

これって結構カッコ悪いのでどうにか出来ないか試行錯誤してみたところ、いい感じの方法が見つかったのでご紹介します。


HTML

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Sample</title>
<!-- 初期状態は非表示 -->
<style type="text/css">body { visibility: hidden; }</style>
<noscript>
<!-- スクリプトが無効な場合は以下の指定がカスケードされる -->
<style type="text/css">body { visibility: visible; }</style>
</noscript>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
jQuery(function($) {
	//
	// DOM の再描画が発生する処理
	//

	// スクリプトから表示状態を指定する
	$("body").css({ visibility: "visible" });
});
</script>
</head>
<body>
<h1>Sample</h1>
</body>
</html>

ポイント

毎度のことながら jQuery を使っていますが、ポイントは以下のとおり。

  1. body の初期状態を visibility: hidden にする
  2. script でコンテンツの操作終了後に bodyvisibility: visible に変更する
  3. JavaScript を無効にしているユーザ向けに noscript 内で visibility: visible に設定する
    • これがないと 2 が実行されないため何も表示されなくなってしまいます

残念なところ

XHTML では head 内に noscript を置くことが出来ないので invalid になってしまいます。一方 HTML5 では (今のところ) head 内の noscript には link, style, meta を格納しても良いことになっているのでこの方法が使えます。(上記コードは HTML5 Validator をきちんとパスします。) 実用志向と言われるだけありますね!

情報

jQuery Easing Plugin のイージング関数を直接利用する方法

はいどうも。ここのところ JavaScript に熱中してます。ということで今回は jQuery の Easing 関数を直接利用する方法をご紹介します。

Easing (イージング) 関数って?

イージング関数とは加速・減速を数式で表したもので、滑らかなアニメーションを表現する際に使われる関数です。jQuery においては jQuery.animate() メソッドの内部で利用されています。標準では linear と swing しか用意されていませんが、jQuery Easing Plugin にて 30 種類ものイージング関数を追加することができます。それぞれの効果についてはこちらのサイトの解説が詳しいです。

そんな素敵なイージング関数ですが jQuery.animate() 以外の処理に応用できたら夢が広がりそうな気がしませんか?

サンプル

Easing 関数の実体は jQuery.easing 下に定義されます。jQuery.easing.イージング名() で関数呼び出しができます。(ショートハンドを使った場合は $.easing.イージング名() です。)

var step = [];
for (var i = 0; i < 10; i++) {
	// 引数リスト: 第一引数は null 固定, 現在のステップ数, 初期値, 目標値, 総ステップ数から 1 を引いたもの
	var value = jQuery.easing.easeOutExpo(null, i, 0, 1, 9);
	step.push(value);
}
console.log(step);

実行結果

[
	0,
	0.5370626438563548,
	0.7856890042867318,
	0.9007874342519875,
	0.9540707971163875,
	0.9787376562472754,
	0.9901568667976963,
	0.9954432459391558,
	0.9978905083224759,
	1
]

0~1 の間を easeOutExpo で 10 分割したものが得られました。今度は easeInOutCirc を使って 0 ~ 100 のあいだを 15 分割してみます。

var step = [];
for (var i = 0; i < 15; i++) {
	// 引数リスト: 第一引数は null 固定, 現在のステップ数, 初期値, 目標値, 総ステップ数から 1 を引いたもの
	var value = jQuery.easing.easeInOutCirc(null, i, 0, 100, 14);
	step.push(value);
}
console.log(step);

実行結果

[
	0,
	0.5128340694606492,
	2.084257625004504,
	4.824604854737435,
	8.967409667585507,
	15.00728938881174,
	24.246062318114358,
	50,
	75.75393768188563,
	84.99271061118826,
	91.03259033241449,
	95.17539514526257,
	97.91574237499549,
	99.48716593053935,
	100
]

このようにイージング関数にて生成した値をアニメーション以外のところに応用することで jQuery.animate() 以外でもイージング関数を使うことが出来ます。何に使うかは……あなた次第!(すぐ思いつくところでは canvas 要素とか?)

というわけでお粗末様でした。

preload preload preload