复制代码
上面的DIV包括了另一个HTML5标签:range input,这个标签可以让用户拖放滑块选择一个数值。回头我们再说在拼图中如何与range input交互。到目前为止ie和firefox并不支持这个标签。
现在就像我上面说过,想要在canvas上绘图,我们需要context。
var context = document.getElementById("puzzle").getContext("2d");复制代码对了我们还需要一个图片,使用例子里自带的,或者找一个和canvas相同大小的图片都行。
var img = new Image();
img.src = 'http://www.brucealderman.info/Images/dimetrodon.jpg';
img.addEventListener('load', drawTiles, false);复制代码加入这个事件是确保图片完成加载后,再把图片放入canvas中。下面我们通过range input设置拼图的数量,数据范围从3到5(几行几列)。
var boardSize = document.getElementById('puzzle').width;
var tileCount = document.getElementById('scale').value;复制代码有了上面两个数值就可以计算一个拼图的大小了
var tileSize = boardSize / tileCount;复制代码OK我们开始创建画板
var boardParts = new Object;
setBoard();复制代码setBoard()的作用是初始化看板,要模拟显示这个画板,我们使用一个二维数组。不过用JavaScript创建这样数组的过程不是很优雅,我们先定义一个平面数组,每个数组再定义一个数组。这个拼图游戏,每一个元素都是一个对象,它带有x和y坐标记录所在的网格位置。因此每个对象有两个坐标,第一个坐标是数组坐标,表示它在画板的位置,另外的坐标是对象的x,y属性,它记录着拼图图片的位置。当这两个坐标相同了就说明位置正确。
为了达到目的,我们在初始化的时候把它们的位置互换。这样拼图就不在正确的位置了。
function setBoard() {
boardParts = new Array(tileCount);
for (var i = 0; i < tileCount; ++i) {
boardParts[i] = new Array(tileCount);
for (var j = 0; j < tileCount; ++j) {
boardParts[i][j] = new Object;
boardParts[i][j].x = (tileCount - 1) - i;
boardParts[i][j].y = (tileCount - 1) - j;
}
}
emptyLoc.x = boardParts[tileCount - 1][tileCount - 1].x;
emptyLoc.y = boardParts[tileCount - 1][tileCount - 1].y;
solved = false;
}复制代码最后三个变量我们还没有定义
我们必须追踪空白拼图的位置还要记录用户点击的位置
var clickLoc = new Object;
clickLoc.x = 0;
clickLoc.y = 0;
var
emptyLoc = new Object;
emptyLoc.x = 0;
emptyLoc.y = 0;复制代码最后这个变量是指拼图是否完成
var solved = false;复制代码所有的拼图都找到正确的位置后,设置它为true。
现在我们需要一些和解决拼图相关的方法
首先为rang input定义触发事件,当它改变了,我们要重新计算拼图的数量和大小
document.getElementById('scale').onchange = function() {
tileCount = this.value;
tileSize = boardSize /
tileCount;
setBoard();
drawTiles();
};复制代码还要追踪鼠标经过的拼图以及哪个拼图被点击
document.getElementById('puzzle').onmousemove = function(e)
{
clickLoc.x = Math.floor((e.pageX - this.offsetLeft) /
tileSize);
clickLoc.y = Math.floor((e.pageY -
this.offsetTop) / tileSize);
};
document.getElementById('puzzle').onclick
= function() {
if (distance(clickLoc.x, clickLoc.y,
emptyLoc.x, emptyLoc.y) == 1) {
slideTile(emptyLoc, clickLoc);
drawTiles();
}
if (solved)
{
alert("You solved
it!");
}
};复制代码有一些浏览器会在重画画板之前弹出对话框,为了防止它的发生,一定要用延迟。
if (solved) {
setTimeout(function() {alert("You solved
it!");}, 500);
}复制代码当一个拼图被点击时,我们要知道它的四周是否可以移动。判断的方法是当前位置到空白位置的总距离为1时就可以移动。
简单点说就是x相同要判断y的距离是否为1,y相同要判断x的距离是否为1。
function distance(x1, y1, x2, y2) {
return Math.abs(x1 -
x2) + Math.abs(y1 - y2);
}复制代码移动拼图的做法是,我们复制被点击拼图的坐标到空位置。然后把点击位置设置成空白坐标。
function slideTile(toLoc, fromLoc) {
if (!solved)
{
boardParts[toLoc.x][toLoc.y].x =
boardParts[fromLoc.x][fromLoc.y].x;
boardParts[toLoc.x][toLoc.y].y =
boardParts[fromLoc.x][fromLoc.y].y;
boardParts[fromLoc.x][fromLoc.y].x = tileCount -
1;
boardParts[fromLoc.x][fromLoc.y].y = tileCount -
1;
toLoc.x =
fromLoc.x;
toLoc.y =
fromLoc.y;
checkSolved();
}
}复制代码一旦拼图移动了,我们还要检查一下拼图是否全部在正确的位置。
function checkSolved() {
var flag =
true;
for (var i = 0; i < tileCount; ++i)
{
for (var j = 0; j <
tileCount; ++j)
{
if
(boardParts[i][j].x != i || boardParts[i][j].y != j)
{
flag =
false;
}
}
}
solved = flag;
}复制代码如果有一个拼图不正确函数就会返回false,否则返回true。
最后,重绘被点击的拼图到新的位置。
function drawTiles() {
context.clearRect ( 0 , 0 , boardSize , boardSize );
for (var i = 0; i < tileCount; ++i) {
for (var j = 0; j < tileCount; ++j) {
var x = boardParts[i][j].x;
var y = boardParts[i][j].y;
if(i != emptyLoc.x || j != emptyLoc.y || solved == true) {
context.drawImage(img, x * tileSize, y * tileSize, tileSize, tileSize,
i * tileSize, j * tileSize, tileSize, tileSize);
}
}
}
}复制代码当画拼图时,这个函数可以防止填充画板时匹配空的位置,因为在游戏中用户可以选择不同的难度。
转自天地会