Goban 0.2

マウスをクリックすると碁石を自由に置けるようにしました。一度置くと取り除くことはできません。配置はまだきれいに揃えていません。

実行結果

Goban 0.2 には HTML5 Canvas が必要です。

ソース

"expand source" を押すと展開されます。元に戻すには、このページを再ロードしてください。

goban02.html

省略します。

goban02.js

定義とメインです。ロードの際に実行されます。イメージは前もってロードするように変更しました。
/**
 * Goban - 碁盤イメージの描画
 * @version 0.2
 * @author たかはしのんき
 * 
 */

// キャッシュにイメージを読み込んでおく
var imgbb = new Image(134, 134);		// 黒の碁笥 (black bowl)
imgbb.src = "img/blackbowl134.png";
var imgwb = new Image(134, 134);		// 白の碁笥 (white bowl)
imgwb.src = "img/whitebowl134.png";
var imgl = new Image(110, 110);			// 碁笥の蓋 (lid)
imgl.src = "img/lid110.png";
var imgb = new Image(22, 22);			// 黒石 (white stone)
imgb.src = "img/black22.png";
var imgw = new Image(22, 22);			// 白石 (black stone)
imgw.src = "img/white22.png";

// キャッシュに効果音を読み込んでおく
var ext = audioExt();
var auClick = new Audio("se/button6."  + ext);
var auSwitch = new Audio("se/se_sad05." + ext);
var auError = new Audio("se/se_sad08." + ext);

// 共通に使う定数と変数
var BLACK = 1;
var WHITE = 2;
var turn;
var context;

/**
 * ロード時に碁盤イメージを描画します。
 */
window.onload = function() {
	// キャンバスとコンテキスト取得
	var canvas = document.querySelector('#table');
	context = canvas.getContext('2d');
	// 座標 (270, 50) にタイトルを表示
	context.fillStyle = "white";
	context.font = "bold 24px Arial";
	context.fillText("Goban 0.2", 270, 50);
	// 座標 (50, 110) に白の碁笥のイメージを表示
	drawObject(50, 110, 20, imgwb, context);
	// 座標 (120, 300) に白の碁笥の蓋のイメージを表示
	drawObject(120, 300, 8, imgl, context);
	// 座標 (400, 300) に黒の碁笥のイメージを表示
	drawObject(400, 300, 20, imgbb, context);
	// 座標 (410, 110) に黒の碁笥の蓋のイメージを表示
	drawObject(410, 110, 8, imgl, context);
	// 座標 (435, 135) に黒石のイメージを表示
	drawObject(435, 135, 2, imgb, context);
	// 座標 (450, 150) に白石のイメージを表示
	drawObject(450, 150, 2, imgw, context);
	// 座標 (210, 110) に6路盤の碁盤を表示
	drawBoard(210, 110, 6, 9, context);
	// 黒番から
	turn = BLACK;
	// クリックイベント登録
	canvas.addEventListener("click", onClick, false);
};

マウスイベントハンドラーを追加しました。
/**
 * マウスイベントハンドラー
 * とりあえず碁石を置きます。
 * @since 0.2
 */
function onClick() {
	var mx = event.offsetX;
	var my = event.offsetY;
	if (turn == BLACK) 
		drawObject(mx - 11, my - 11, 2, imgb, context);
	else
		drawObject(mx - 11, my - 11, 2, imgw, context);
	auClick.play();
	turn = 3 - turn;
}
効果音のために有効な拡張子を調べる処理を追加しました。
/**
 * ブラウザで有効なAudioの拡張子を獲得
 * @since 0.2
 */
function audioExt() {
    var ext = "";
    var audio = new Audio();
 	if (audio.canPlayType("audio/ogg") == 'maybe')
 		ext="ogg";
    else if (audio.canPlayType("audio/mp3") == 'maybe')
    	ext="mp3";
    else if (audio.canPlayType("audio/wav") == 'maybe')
    	ext="wav";
    return ext;
}

円形オブジェクトを描画します。変更はありません。
/**
 * 円形オブジェクトを描画します。
 * @param x 左端座標
 * @param y 上端座標
 * @param s 影の長さ(高さ/2)
 * @param img オブジェクトのイメージ
 * @param context 描画先のコンテキストを指定します。
 * @since 0.1
 */
function drawObject(x, y, s, img, context) {
	var r = img.width / 2;
	context.beginPath();
	context.arc(x + r + s, y + r + s, r, 0, 2 * Math.PI);
	context.fillStyle = "black";
	context.globalAlpha = 0.5;
	context.fill();
	context.globalAlpha = 1.0;
	context.drawImage(img, x, y);
}

碁盤を描画します。星の描画はまだ入れていません。格子がぼやけないよう、stroke() から fill() に変更しました。
/**
 * 碁盤を描画します。
 * @param x 左端座標
 * @param y 上端座標
 * @param ro 路数
 * @param s 影の長さ(高さ/2)
 * @param context 描画先のコンテキストを指定します。
 * @since 0.1
 */
function drawBoard(x, y, ro, s, context) {
	var dx = 22;
	var dy = 24;
	var width = dx * (ro + 1);
	var height = dy * (ro + 1);
	var gwidth = dx * (ro - 1) + 2;
	var gheight = dy * (ro - 1) + 2;
	var gx = x + Math.floor((width - gwidth) / 2);
	var gy = y + Math.floor((height - gheight) / 2);
	// 碁盤の影
	context.beginPath();
	context.moveTo(x, y);
	context.lineTo(x + width, y);
	context.lineTo(x + width + s, y + s);
	context.lineTo(x + width + s, y + height + s);
	context.lineTo(x + s, y + height + s);
	context.lineTo(x, y + height);
	context.closePath();
	context.fillStyle = "black";
	context.globalAlpha = 0.5;
	context.fill();
	// 碁盤
	context.beginPath();
	context.rect(x, y, width, height);
	context.fillStyle = "burlywood";
	context.globalAlpha = 1.0;
	context.fill();
	// 木目
	drawWoodGrain(x, y, width, height, context);
	// 格子
	context.fillStyle = "black";
	var x1, y1, lwidth;
	// (横の格子線)
	x1 = gx;
	for (var row = 1; row <= ro; row++) {
		if (row == 1)
			y1 = gy + (row - 1) * dy;
		else
			y1 = gy + 1 + (row - 1) * dy;
		if (row == 1 || row == ro)
			lwidth = 2;
		else
			lwidth = 1;
		context.beginPath();
		context.rect(x1, y1, gwidth, lwidth);
		context.fill();
	}
	// 縦の格子線
	y1 = gy;
	for (var col = 1; col <= ro; col++) {
		if (col == 1)
			x1 = gx + (col - 1) * dx;
		else
			x1 = gx + 1 + (col - 1) * dx;
		if (col == 1 || col == ro)
			lwidth = 2;
		else
			lwidth = 1;
		context.beginPath();
		context.rect(x1, y1, lwidth, gheight);
		context.fill();
	}
}

木目を描画します。クリッピングの範囲を縦横に1ドットずつ広げました。
/**
 * 木目を描画します。
 * @param x 左端座標
 * @param y 上端座標
 * @param width 描画する幅
 * @param height 描画する高さ
 * @param context 描画先のコンテキストを指定します。
 * @since 0.1
 */
function drawWoodGrain(x, y, width, height, context) {
	context.strokeStyle = "rgb(192, 153, 86)";	// 木目の色
	var xb0 = x;
	var xb1 = x + width;
	var yb0 = y;
	var yb1 = y + height;
	var xo, yo, xw, yw;
	var xc = xb0 + (xb1 - xb0) * 5 / 7;			// 木目の中心
	var yc = yb0 + (yb1 - yb0) * 5 / 4;
	for (var rw = 5; rw <= 460; rw += 5) {
		xo = xc + rw;
		yo = yc;
		for (var theta = 0; theta <= 2 * Math.PI;
				theta += 2 * Math.PI / 19) {
			xw = xc + Math.floor(rw * Math.cos(theta));
			yw = yc + 13 * Math.floor(rw * Math.sin(theta));
			var xw0 = xo, xw1 = xw, yw0 = yo, yw1 = yw;
			if (yw1<yb0 && yb0<yw0) {			//上辺でクリッピング
				xw1 = xw0+(xw1-xw0)*(yb0-yw0)/(yw1-yw0);
				yw1 = yb0;
			} else if (yw0<yb0 && yb0<yw1) {	//上辺でクリッピング
				xw0 = xw1+(xw0-xw1)*(yb0-yw1)/(yw0-yw1);
				yw0 = yb0;
			}
			if (yw1<yb1 && yb1<yw0) {			//下辺でクリッピング
				xw0 = xw1+(xw0-xw1)*(yb1-yw1)/(yw0-yw1);
				yw0 = yb1;
			} else if (yw0<yb1 && yb1<yw1) {	//下辺でクリッピング
				xw1 = xw0+(xw1-xw0)*(yb1-yw0)/(yw1-yw0);
				yw1 = yb1;
			}
			if (xw1<xb0 && xb0<xw0) {			//左辺でクリッピング
				xw1 = xb0;
				yw1 = yw0+(yw1-yw0)*(xb0-xw0)/(xw1-xw0);
			} else if (xw0<xb0 && xb0<xw1) {	//左辺でクリッピング
				xw0 = xb0;
				yw0 = yw1+(yw0-yw1)*(xb0-xw1)/(xw0-xw1);
			}
			if (xw1<xb1 && xb1<xw0) {			//右辺でクリッピング
				xw0 = xb1;
				yw0 = yw1+(yw0-yw1)*(xb1-xw1)/(xw0-xw1);
			} else if (xw0<xb1 && xb1<xw1) {	//右辺でクリッピング
				xw1 = xb1;
				yw1 = yw0+(yw1-yw0)*(xb1-xw0)/(xw1-xw0);
			}
			if (xb0<=xw0 && xw0<=xb1 && xb0<=xw1 && xw1<=xb1 &&
					yb0<=yw0 && yw0<=yb1 && yb0<=yw1 && yw1<=yb1) {
				context.beginPath();
				context.moveTo(xw0, yw0);
				context.lineTo(xw1, yw1);
				context.stroke();
			}
			xo = xw;
			yo = yw;
		}
	}
}