第6回 画像を使ったジグソーパズルを作ってみよう – jQuery入門

2009 年 5 月 7 日 投稿者: naga3

jQuery入門第6回目は、画像を使った簡易ジグソーパズルゲームを作ってみます。簡易的なものなので、ピースの凹凸はありません。今回のポイントはCSSによって画像の一部分のみを指定する方法(background-position)です。まずはプレイ画面をどうぞ。

簡易ジグソーパズル

2つのピースをクリックすると入れ替わります。元の絵に戻すとクリアです。

簡易ジグソーパズルのソース

使用するリソース(右クリックで保存してください)
HTML
使用する画像
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.3.2");</script>


<style type="text/css">
div.piece {
    width: 80px;
    height: 80px;
    background-image: url('pict.jpg');
    position: absolute;
}
</style>

<script type="text/javascript">
var sel = -1;        // 現在選択されているピース番号
var pos = [];        // 各ピースの現在位置

$(function() {
    // シャッフルする。
    for (var i = 0; i < 16; i++) pos[i] = i;
    for (var i = 16; i > 0; i--) {
        var j = Math.floor(Math.random() * i);
        swap(i - 1, j);
    }

    // ピースを配置する。
    for (var i = 0; i < 16; i++) {
        $('body').append('<div id="piece' + i + '" class="piece"></div>');
        $('#piece' + i).css({
            backgroundPosition: '-' + getx(i) + 'px -' + gety(i) + 'px',
            left: getx(pos[i]), top: gety(pos[i])
        })
    }

    // ピースをクリックしたとき
    $('div.piece').click(function() {
        var no = this.id.substring(5);
        if (sel == no) {
            // 自分自身が選択されているときは選択をキャンセルする。
            $(this).fadeTo(100, 1);
            sel = -1;
        } else if (sel == -1) {
            // 何も選択されていないときは選択する。
            $(this).fadeTo(100, 0.5);
            sel = no;
        } else {
            // 他のピースが選択されているときは入れ替える。
            swap(no, sel);
            $('#piece' + sel).fadeTo(100, 1).animate({left: getx(pos[sel]), top: gety(pos[sel])});
            $(this).animate({left: getx(pos[no]), top: gety(pos[no])}, function() {
                // アニメーション終了時にクリア判定する。
                var clear = true;
                for (var i = 0; i < 16; i++) if (pos[i] != i) clear = false;
                if (clear) alert("クリア!");
            });
            sel = -1;
        }
    });
});

// ピースの番号から座標を得る。
function getx(n) {return (n % 4) * 80;}
function gety(n) {return Math.floor(n / 4) * 80;}

// ピースの配列を入れ替える。
function swap(i, j) {
    var tmp = pos[i];
    pos[i] = pos[j];
    pos[j] = tmp;
}
</script>
</head>
<body>
</body>
</html>

プログラムの解説

ピースのスタイルシート

div.piece {
    width: 80px;
    height: 80px;
    background-image: url('pict.jpg');
    position: absolute;
}
各ピースに割り当てられているCSSです。pict.jpgは320×320ピクセルの画像なのですが、widthとheightプロパティによって幅と高さを80ピクセルに固定し、縦横とも4分割で表示するための準備をしています。さらにpositionをabsoluteに設定することによって画面上の自由な位置にピースを表示できるようにします。

グローバル変数

var sel = -1;        // 現在選択されているピース番号
var pos = [];        // 各ピースの現在位置
ピースには0~15の番号が付いています。画像に対するピース番号は以下の通り。
0123
4567
891011
12131415
変数selには現在選択されているピース番号が入ります(どれも選択されていないときは-1)。配列posは、少し分かりにくいですが、添え字のピース番号の現在位置が入ります。例えばpos[0]→12だった場合、完成したときに左上にあるべきピースが、現在は左下にある、ということになります。

ピースのシャッフル

for (var i = 0; i < 16; i++) pos[i] = i;
まず初期値として、完成形で配列posを作ります(添え字が中の値と等しい)。
for (var i = 16; i > 0; i--) {
    var j = Math.floor(Math.random() * i);
    swap(i - 1, j);
}
その後、配列posの中身がバラバラになるようにシャッフルしています。「Fisher-Yates」というアルゴリズムを使っていますので、詳しくはググってみてください。

画面上にピースを配置する

for (var i = 0; i < 16; i++) {
    $('body').append('<div id="piece' + i + '" class="piece"></div>');
    $('#piece' + i).css({
        backgroundPosition: '-' + getx(i) + 'px -' + gety(i) + 'px',
        left: getx(pos[i]), top: gety(pos[i])
    })
}
今回のポイントである、画像の一部をCSSで指定して画面上に配置している部分です。「piece + ピース番号」というidを付けたdiv要素をbodyに追加し、そのdiv要素にCSSを設定しています。
background-positionは背景画像の表示開始位置を決めるプロパティなのですが、マイナスの値を設定すると開始位置を逆方向に設定する、つまり画像の途中から表示させることが可能になるのです。例えば「background-position: -80px -160px」とすると、画像の左端から80ピクセル、上端から160ピクセルの位置から画像を表示させることができます。jQueryで指定する場合はプロパティにハイフンが使えないので「backgroundPosition」と書くことに注意してください。
getxとgetyは、最下部に定義してありますが、ピース番号を指定するとその座標を返してくれる関数です。
「left: getx(pos[i]), top: gety(pos[i])」によって、シャッフルされた位置にピースを持ってきています。

ピースをクリックしたときの処理

var no = this.id.substring(5);
それぞれのピースのidは「piece + ピース番号」なので、変数noにはピース番号が入ります。
if (sel == no) {
    // 自分自身が選択されているときは選択をキャンセルする。
    $(this).fadeTo(100, 1);
    sel = -1;
選択されているピースを再度選択したときは、選択解除しています。fadeToは透明度を変更するjQueryのメソッドで、透明度は0~1(0が完全に透明、1が完全に不透明)で指定します。今回は100msかけて透明度1になります。
} else if (sel == -1) {
    // 何も選択されていないときは選択する。
    $(this).fadeTo(100, 0.5);
    sel = no;
何も選択されていないときはクリックされたピースを選択状態にしています。100msかけて透明度0.5(半分透けている状態)にしています。
swap(no, sel);
ピースを交換する場合は、まずpos配列でクリックされたピース番号と選択されているピース番号を入れ替えます。
$('#piece' + sel).fadeTo(100, 1).animate({left: getx(pos[sel]), top: gety(pos[sel])});
選択されているピースの透明度を元に戻し、クリックされたピースの位置へ移動するアニメーションを行なっています。
$(this).animate({left: getx(pos[no]), top: gety(pos[no])}, function() {
    // アニメーション終了時にクリア判定する。
    var clear = true;
    for (var i = 0; i < 16; i++) if (pos[i] != i) clear = false;
    if (clear) alert("クリア!");
});
クリックされたピースを選択されているピースの位置へ移動するアニメーションを行なっています。さらにアニメーションが終わったときのコールバック関数を指定し、そこでクリア条件のチェックをしています。クリア条件は、pos配列全ての要素の添え字が値と等しい時です。

まとめ

・「position: absolute」によって絶対位置指定をし、「left」と「top」プロパティによって位置を指定する。
・「background-position」プロパティにマイナスの値を指定することによって、画像の途中から表示を始めることができる。
・「background-position」プロパティをjQueryで指定するときは「backgroundPosition」と書く。

この記事へのトラックバックURL

コメントをどうぞ