005_blind.png はいどうも~。
雪の日は電車遅延でテンションが下がりがちなエンジニアの吉田です。

あたくし、通勤時間が通常だとだいたい1時間45分なのですが、
電車遅延があると、だいたい2時間を越える程度の通勤時間となり、
会社に到着した時点で、既にぐったりしていたり、していなかったり。

そんな中、家から最寄駅に着いて最初にげんなりするキッカケとなる
のが、駅にある電光掲示板で誇らしげにチラついていらっしゃる
【横須賀線 遅延】」というオレンジ色の掲示だったりします。

特に、家から駅までダッシュしたあとにお目にかかれたときなんかは、
凄まじい威力を発揮して頂けますね。洗練された脱力感を体験できる。

今回は、この電光掲示板を見た時にテンションが下がる気分を是非
共有したいと思い、HTML5のCanvasを使って表現してみます。


表現してみた結果、こんな感じのものが出来上がりました。


 ※Windows7 + Firefox10、Chrome17以外は動作確認してません。
 ※お使いのPCの性能によっては、ブラウザの応答がなくなるかもしれません。
 ※特にIE9はレンダリング遅いのでブラウザ固まっても怒らないでください。
 ※【追記】描画が遅すぎたので、このサンプルでは、モザイク化処理を省いています。
 ※【追記】本来はもっと輪郭がボヤけたような感じになるです。

もうなんか見ているだけでテンション下がる感じがします。
※ちなみに、この記事も遅延している電車の中で執筆しています。


という前置きの話はどうでもいいとして、今回の記事は、HTML5のCanvas
で電光掲示板もどきを作ってみたので、その作り方を紹介するというのが
本当の主旨だったりします。
(最近コードの伴わない記事が多かった自分への戒めの意も込め。。)


そうそう、記事の最後に、テキストを自分で入力できる形のものを、
貼り付けたので(jsdoitで)、みなさんの通勤で利用する路線や
駅名に書き換えて、是非テンションを下げてみてくださいね。


というわけで、作り方は、続きからどうぞ。



電光掲示板の作り方を知る


まず、電光掲示板をWebで表現したいという欲求を満たすためには、
電光掲示板っぽい見た目の作り方を知る必要があります。

デザイナーさんなんかだと、ちゃちゃっと作れちゃうのかもしれませんが
エンジニアの私はググるところから始めなければなりません。

「Photoshop 電光掲示板」などという単語でググると、下記のサイトが
ひっかかりました。

電光掲示板|3倍早くなるためのDTP講座

PhotoshopではなくIllustratorでの記事でしたが、念願の作り方を
知ることができました。
これで、テンションの下がる気持ちを伝えることができそうです。

電光掲示板を作るには、どうやら、3段階の手順を踏めば良さそうです。

 1.テキストを描く
 2.テキストにピクセレート処理をかける
 3.電光掲示板のドットっぽくする

電光掲示板の作り方がわかったので、早速作業に取り掛かります。


テキストを描く


Webでテキストを描くには、文字をHTMLに書くだけですみますが、
その後にピクセレート処理をすることを考えると、どうやら、
HTML5のCanvasを使う必要がありそうです。

というわけで、HTML5のCanvasとJavaScriptを使用して、テンション
を下げる訴求効果の高い「【横須賀線 遅延】」という文字を
描いてみたいと思います。

HTML

<div id="wrapper1" class="wrapper">
  <canvas id='board1' class="board" width="512" height="64"></canvas>
</div>

CSS

div#wrapper1 {
	height: 64px;
	width: 578px;
	overflow: hidden;
	position:relative;
	background-color: #000;
}
canvas#board1 {
	border: solid 0px #333;
	background-color: #000;
	position: absolute;
	left: 0px;
}

JavaScript (jQueryを使用)

function drawText(){
	// canvas要素を取得
	var canvas = $('canvas#board1');
	var context = canvas.get(0).getContext('2d');
	// 一旦canvasをクリア
	context.clearRect(0, 0, canvas.width(), canvas.height());
	// 灰色の明朝体で「【横須賀線 遅延】」を描く
	context.fillStyle = "gray";
	context.font = "64px 'MS 明朝'";
	context.textAlign = "left";
	context.textBaseline = "top";
	context.fillText( "【横須賀線 遅延】", 0, 0);
}

※コードは一部抜粋のため、そのまま動くものではありません。

以下のようなイメージで、テキストを描画することが出来ました。

001_drawtext.png


ピクセレート処理をかける


テキストを描画できたところで、次にピクセレート処理をかけます。
別の言い方をすると、モザイク処理ってやつですね。

canvasでモザイク処理をするには、どうすれば良いかというと、
「特定のエリアごとに、そのエリアの代表色を決めて、その色で
そのエリアを塗りつぶす」という感じの処理をcanvasの全体に
適用してやればイケそうな気がします。

一応、その前に参考になるプログラムがないか探してみたところ、
jsdoitにモザイク処理をかけているサンプルがあったので、こちらも
参考にしてみました。

 forked: 部分モザイク - jsdo.it - Share JavaScript, HTML5 and CSS

というわけで、ピクセレート処理のプログラムは以下のような感じです。

3ピクセル×3ピクセルの正方形の範囲(9ピクセル)ごとに、その正方形の
RGB値の平均値を算出し、その平均値で正方形を塗りつぶす、という処理を
canvasの全てに対して行っています。


JavaScript

var BLOCK_SIZE = 3;
/**
 * ピクセレート処理
 */
function _toMosaic(canvas, context){
	var _width = canvas.width();
	var _height = canvas.height();
	
	(function(){
		var i=0, j=0;
		var x=0, y=0, w=0, h=0;
		var tiles_w = _width / BLOCK_SIZE;
		var tiles_h = _height / BLOCK_SIZE;
		var block_image;
		
		for(i=0; i<tiles_w; i++){
			for(j=0; j<tiles_h; j++){
				w = BLOCK_SIZE;
				h = BLOCK_SIZE;
				x = (i * w);
				y = (j * h);
				
				block_image = context.getImageData(x, y, w, h);
				rgb = _getAvgRgb(block_image);
				
				context.fillStyle = 'rgb('+rgb[0]+','+rgb[1]+','+rgb[2]+')';
				context.fillRect(x, y, w, h);
			}
		}
	})();
}

/**
 * 特定の範囲のRGB平均値を求める
 */
function _getAvgRgb(block_image){
	
	var pixels = block_image.data;
	var r=0,g=0,b=0;
	var rgb;
	
	// LEDの明るさをブロック内の全ピクセルの平均で算出する
	// 各ピクセルは、Red/Green/Blue/Alpha の4個の配列になっている
	var len = pixels.length/4;
	for(var i=0; i<len; i++){
		r += pixels[i*4];
		g += pixels[i*4+1];
		b += pixels[i*4+2];
	}
	r = parseInt(r/len,10);
	g = parseInt(g/len,10);
	b = parseInt(b/len,10);
	
	return [r,g,b];
}

この処理をすると、以下のようなイメージが描画されます。

002_mosaic.png


ちょっと明るくする


ピクセレート処理が終わりましたが、灰色で描画したせいか
ちょっと暗い感じになってしまいました。。。

なので、ピクセレートした各ブロックごとに、少しだけ明るく
なるような調整をしてあげました。


JavaScript

/**
 * 明るさをちょっと調整する
 */
function _adjustBlightness(r,g,b){
	// 平均値だけだと全体的にぼやけた感じに見えるので、
	// 閾値を超えている場合は、さらにちょっと明るくする
	var threshold = 20;
	if( (r>threshold) && (g>threshold) && (b>threshold) ){
		r = r+160; 
		g = g+160; 
		b = b+160;
	}else{
		r = r+20; 
		g = g+20; 
		b = b+20;
	}
	
	// 光っていないLEDは、うっすらと見えるようにする
	if( (r==0) && (g==0) && (b==0) ){
		r = 40; 
		g = 40;
		b = 40;
	}
	
	return [r,g,b];
}

これで、以下のようなイメージが描画されます。

003_bright.png


オレンジ色にする


明るさの調整もできたところで、色合いを調整してみます。
電光掲示板と言えば、オレンジ色です。

なので、ブロックごとにオレンジ色にしてやりましょう。

グレーの画像をオレンジ色にするためには、それぞれのブロック
ごとに、R値とG値とB値に対して255の逆数をとって、オレンジ色の
RGB値(243, 152, 0)を乗算してやれば、きっとできるはずです。

※ちなみに、セピア色にする画像工学の数式を参考にしています。
セピア調化|画像処理におけるアルゴリズム


JavaScript

/**
 * RGB値を受け取って、単色化する
 */
function _colorize(r,g,b) {
	// 電光掲示板なので、オレンジ色(243, 152, 0)にする
	r = parseInt(r / 255 * 243, 10);
	g = parseInt(g / 255 * 152, 10);
	b = parseInt(b / 255 *   0, 10);
	
	return [r,g,b];
}

これで、色がオレンジになりました。

004_orange.png


電光掲示板のドットっぽくする


オレンジ色でモザイクがかったイメージが出来たので、次に
電光掲示板のドットっぽい感じにしてみたいと思います。

ドットっぽい感じを出すためには、敷き詰めると格子状になる
ような画像を上書きしてやれば良さそうな感じがします。

そこで、「□」のような感じの画像(中抜き隙間の部分は2pxほど)
を用意して、敷き詰めようとしたのですが、どうも描画処理が
遅いらしく、ブラウザの応答が悪くなくなってしまいました。

なので、別の方法を考えてみたところ、細い直線を縦と横に
一定間隔で描画してやれば、それらしく見えるんじゃないかと
思ったので、その方法でやってみることにします。

JavaScript

/**
 * 電光掲示板のような格子状の罫線を引く
 */
function _toBlind(canvas, context){
	
	var _width = canvas.width();
	var _height = canvas.height();
	
	(function(){
		var i=0, j=0;
		var x=0, y=0, w=0, h=0;
		var tiles_w = _width / BLOCK_SIZE;
		var tiles_h = _height / BLOCK_SIZE;

		context.beginPath();
		context.lineWidth = 0.9;
		
		// 縦線を引く
		for(i=0; i<tiles_w+1; i++){
			w = BLOCK_SIZE;
			x = (i * w);
			
			context.moveTo(x, 0);
			context.lineTo(x, _height);
			
		}
		
		// 横線を引く
		for(j=0; j<tiles_h+1; j++){
			h = BLOCK_SIZE;
			y = (j * h);
			
			context.moveTo(0, y);
			context.lineTo(_width, y);
		}
		
		context.stroke();
	})();
}

これで、格子状の罫線を引くことができました。
なんとなく、電光掲示板ぽい感じも出ているような気がします。
(そうでもない・・・?)

005_blind.png


スクロールさせる


電光掲示板ぽい見た目が表現できたところで、電光掲示板の
スクロールの動きを入れてやることにします。

電光掲示板ぽい動きを実現するために、参考になるプログラムが
ないか探してみたところ、jsdoitにスクロールのサンプルコード
があったので、これを参考にしてみました。

電光掲示板的なもの - jsdo.it - Share JavaScript, HTML5 and CSS

やり方は、canvasをdivの子要素にしてあげて相対位置指定にし、
canvasをdivの右側の枠外から徐々に左に移動させ、左側の枠外に
出た場合は右側の枠外に戻す、という感じです。
※実際は、頭に全角スペースを埋めるようにしているので、canvas
自体は枠内から移動するようにしています。

JavaScript

/**
 * スクロールする
 */
function _doScroll(canvas, context, wrapper){
	var wrapper_w = wrapper.width();
	var canvas_w = canvas.width();
	var x0 = 0;
	var x  = x0;
	setInterval(function(){
		if(x < -canvas_w+WRAPPER_WIDTH) x = x0;
		canvas.css('left', x+'px');
		x -= 3;
	}, 20);
	
}

これで、冒頭に表示した電光掲示板の完成です!ひゃっほーい。



電光掲示板の内容を差し替える


今回の電光掲示板もどきは、jsdo.itにも置いておきました。

jsdo.itに置いてあるものは、テキストボックスからテキストを
指定することができるので、皆さんの通勤路線や駅名に変更して
いただくことで、より一層テンションの下がり具合をお伝えすること
ができるかと思います。


HTML5のCanvasとJSで電光掲示板 - jsdo.it - share JavaScript, HTML5 and CSS



おわりに


テンションの下がる気持ちは伝わりましたでしょうか。
今度は、Arduinoとか使って本物の電光掲示板でお伝えすることに挑戦できればと思うです。

あと、今回のはcanvasの描画が重いので、その辺は改善の余地がありそうな気がしてます。
誰かがforkして改善してくれることを期待しつつ、今回はこの辺で。

デワデワ。