datchの日記

気がついたら社会人。気になる技術的なことについて少しずつ書いていけたらと思っております。

【Wonderfl移植計画】AquaTypography【7作目】

wonderflのFavoriteTop100にランクインしている作品をCreateJSを使って移植しようという計画。

ライセンスはMITなのを確認していますが、
制作者様の方から何かアクションがあった場合は削除させていただきます。

87位 AquaTypography

移植元のFlashソースコード

CreateJSのデモ

色々と修正の余地があるので解説編で修正したコードあげます。今回はバグに苦しんだ

// EaselJS 0.7
// BitmapData 1.0.0
// -- http://kudox.jp/java-script/createjs-easeljs-bitmapdata
var stage;

var W;
var H;

var font = "Aqua";

var N = 2000;

var _it;
var _submit;
var _tf;

var _particles; 
var _tlim;		// [終了時刻]
var _canvas;	// [ここにかく]
var _t;			// [経過時間]
var _offset;	// [色位相のオフセット]


var Particle = (function()
{
	function Particle(xCoes, yCoes)
	{
		this.xCoes = xCoes;
		this.yCoes = yCoes;
	}
	return Particle;
})();

// [@see http://www5d.biglobe.ne.jp/~stssk/maze/spline.html]
var Spline = (function()
{
	function Spline()
	{}

	return Spline;
})();

Spline.calc = function(cs, t)
{
	var p = Math.floor(t);
	if(p >= cs.a.length) --p;
	if(p < 0) p = 0;
	var dt = t - p;
	return cs.a[p] + (cs.b[p] + (cs.c[p] + cs.d[p] * dt) * dt) * dt;
};

Spline.calcCoordinate = function(a)
{
	var n = a.length;

	var b = Array();
	var c = Array();
	var d = Array();
	var w = Array();

	var i;

	c.push(0);
	for(i = 1; i < n - 1; ++i)
	{
		c.push(3 * (a[i + 1] - 2 * a[i] + a[i - 1]));
	}
	c.push(0);

	w.push(0);

	for(i = 1; i < n - 1; ++i)
	{
		var l = 4.0 - w[i - 1];
		c[i] = (c[i] - c[i - 1]) / l;
		w.push(1.0 / l);
	}
	for(i = n - 2; i > 0; --i)
	{
		c[i] -= c[i + 1] * w[i];
	}

	for(i = 0; i < n - 1; ++i)
	{
		d.push((c[i + 1] - c[i]) / 3.0);
		b.push(a[i + 1] - a[i] - c[i] - d[i]);
	}
	b.push(0);
	d.push(0);

	return {a : a, b : b, c : c, d : d};
}

window.onload = init;

/**
* この関数にコンストラクタやonFontLoadedの中身を記述する
*/
function init()
{
	stage = new createjs.Stage('canvas');
	W = stage.canvas.width;
	H = stage.canvas.height;

	// コンストラクタ
	// でも中身はほとんどテキストフィールドやボタン、フォントの配置などでHTML側で行う
	_canvas = new createjs.BitmapData(null, W, H, 0x000000);
	stage.addChild(new createjs.Bitmap(_canvas.canvas));
	_it = document.getElementById('text');
	_it.value = 'jsdo.it';
	
	_submit = document.getElementById('go');
	_submit.onClick = onSubmit;
	_submit.enable = false;

	// onFontLoaded
	// テキストフィールドはCreateJSにはないので直接TEXTをBitmapにして扱う
	_tf = new createjs.Text("12", "100px Aqua", "#000000");
	stage.addEventListener('stagemousedown', onClick);
	// canvasを黒の背景で初期化する
	stage.update();

	createjs.Ticker.setFPS(60);

	reset();
}


function onSubmit()
{
	if(_it.text.length == 0) return;
	reset();
}

function onClick()
{
	start();
}

function reset()
{
	if(_it.text == '') return;

	// [白抜き作成]
	var bmds = new Array();
	var str = _it.value;
	for(var i = 0; i < str.length; ++i)
	{
		var c = str[i];
		if(c == ' ' || c == '  ') continue; // [スペースはノーカウント]
		_tf.text = c;
		var bmd = textFieldToBitmap(_tf, 4);
		var blurred = new createjs.BitmapData(null, W, H, 0xffffff);

		// [元のBitmapDataにblurをかけ、さらに元のBitmapDataを描く、というのを繰り返しで
		// 文字の周囲を濃くする]
		for(var j = 0; j < 10; ++j)
		{
			blurred.applyFilter(blurred, blurred.rect, new createjs.Point(), new createjs.BlurFilter(100, 100));
			blurred.draw(bmd);
		}
		// [んノクアウトォ!]
		// createjs.BitmapDataにはdrawするときのブレンドモードが指定出来ないので
		// blurredの値を反転する関数を用意した
		// @note
		// この関数は参照渡し(newした変数が参照渡しされる)の時のみ反映されるので、注意が必要
		//invertBitmapData(bmd);
		blurred.draw(bmd);
		bmd.dispose();

		bmds.push(blurred);
	}

	// [パーティクルの経路をつくる]
	// [できた白抜きの黒い部分を経由するように]
	_particles = new Array();
	for(i = 0; i < N; ++i)
	{
		var xs = new Array();
		var ys = new Array();
		var r = (W + H) / 2;
		var theta = Math.random() * 2 * Math.PI;

		// [初期値はstageの外]
		xs.push(W / 2 + r * Math.cos(theta));
		ys.push(H / 2 + r * Math.sin(theta));

		// [各BitmapDataに対して座標をひとつきめる]
		//for(bmd in bmds)
		for(var j = 0; j < bmds.length; ++j)
		{
			bmd = bmds[j];
			while(true)
			{
				var x = Math.random() * W;
				var y = Math.random() * H;
				var pixel = bmd.getPixel(x, y);
				var pc = bmd.getPixel(x, y) & 0xff;
				// [黒いほど採用しやすい]
				if(Math.random() < 1 - pc / 210) break;
			}
			xs.push(x);
			ys.push(y);
		}
		
		// [最後もstageの外]
		xs.push(W / 2 + r * Math.cos(theta));
		ys.push(H / 2 + r * Math.sin(theta));

		// [スプラインの係数を計算して格納]
		var xCoes = Spline.calcCoordinate(xs);
		var yCoes = Spline.calcCoordinate(ys);
		_particles.push(new Particle(xCoes, yCoes));
	}

	// 描画の残りを消すために2秒ではなく5秒追加に変更
	_tlim = bmds.length + 5;

	// [_bmdsは用済み]
	for(i= 0; i < bmds.length; ++i)
	{
		bmds[i].dispose();
	}
	start();
}

function start()
{
	console.log('start');
	_t = 0;
	createjs.Ticker.removeEventListener("tick", onEnterFrame);
	createjs.Ticker.addEventListener("tick", onEnterFrame);
	_offset = Math.random() * 2 * Math.PI;
}

function onEnterFrame()
{
	console.log('enterframe');
	// [パーティクルの描画]
	for(var i = 0; i < _particles.length; ++i)
	{
		p = _particles[i];
		var px = Spline.calc(p.xCoes, _t);
		var py = Spline.calc(p.yCoes, _t);
		//console.log(px, py);
		if(px < 0 || px >= W || py < 0 || py >= H) continue;
		_canvas.setPixel(px, py, 0xffffff);
	}

	// [色減衰]
	// [減衰度をまわすことによって色調を変える]
	_canvas.updateContext();
	_canvas.colorTransform(_canvas.rect,
		new createjs.ColorTransform(
			0.96 + 0.02 * Math.sin(_t + _offset),
			0.96 + 0.02 * Math.sin(_t + Math.PI * 2 / 3 + _offset),
			0.96 + 0.02 * Math.sin(_t + Math.PI * 4 / 3 + _offset),
			1, -0.05, -0.05, -0.05
		)
	);

	_t += 0.009;
	console.log(_t, _tlim);
	if(_t >= _tlim) createjs.Ticker.removeEventListener('tick', onEnterFrame);
	stage.update();
}

// [tfを画面の中央に置いてscale倍してBitmapDataに転写]
function textFieldToBitmap(tf, scale)
{
	scale = scale || 1;
	//var TEXT_WIDTH = 100, TEXT_HEIGHT = 100;
	var TEXT_WIDTH = W, TEXT_HEIGHT = H;
	//var bmd = new createjs.BitmapData(null, W, H);
	var bmd = new createjs.BitmapData(null, W, H, 0xffffff);
	// createjs.BitmapDataの仕様上0x00ffffffという透明な白色の宣言が出来ないので、
	// 以下のようにカラートランスフォームを行っている。
	//bmd.colorTransform(bmd.rect, new createjs.ColorTransform(1, 1, 1, 1, 255, 255, 255));
	var mat = new createjs.Matrix2D();
	mat.scale(scale, scale);
	// テキストフィールドの大きさが100 x 100で定義されているので、定数で100を定義
	//mat.translate(W / 2 - TEXT_WIDTH * scale, H / 2 - TEXT_HEIGHT * scale /2);
	tf.cache(0, 0, TEXT_WIDTH, TEXT_HEIGHT);
	bmd.draw(tf, mat);
	return bmd;
}

// 与えられたcreatejs.BitmapDataの色を反転する
function invertBitmapData(bmd)
{
	var data = bmd.getPixels(bmd.rect);
	for (var i = 0, l = data.length; i < l; i += 4)
	{
		data[i] = 255 - data[i];			// R
		data[i + 1] = 255 - data[i + 1];	// G
		data[i + 2] = 255 - data[i + 2]; 	// B
		data[i + 3] = 255 - data[i + 3];	// A
	}
	bmd.setPixels(bmd.rect, data);
}