HTML5包含许多功能,可将多媒体本身集成到网页中。 这些功能包括canvas元素,这是一个空白面板,可以填充线条图,图像文件或动画。
在本教程中,我将通过创建一个滑动拼图游戏来演示HTML5 canvas的图像处理功能。
要将画布嵌入网页中,请使用<canvas>标记。
<canvas width="480px" height="480px"></canvas>
width和height属性以像素为单位设置画布大小。 如果未指定这些属性,则默认宽度为300px,高度为150px。
在画布上绘制是通过上下文完成的,该上下文通过JavaScript函数getContext()初始化。 W3C指定的二维上下文被适当地称为“ 2d”。 因此,要初始化ID为“ canvas”的画布的上下文,我们只需调用:
document.getElementById("canvas").getContext("2d");
下一步是显示图像。 JavaScript为此只有一个函数drawImage() ,但是有三种方法可以调用此函数。 在最基本的形式中,此函数采用三个参数:图像对象以及与画布左上角的x和y偏移量。
drawImage(image, x, y);
还可以添加另外两个参数,即width和height,以调整图像的大小。
drawImage(image, x, y, width, height);
drawImage()的最复杂形式采用九个参数。 第一个是图像对象。 接下来的四个依次是源x,y,宽度和高度。 其余四个依次是目标x,y,宽度和高度。 此功能提取图像的一部分以在画布上绘制,并在必要时调整其大小。 这使我们可以将图像视为精灵表。
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
对于所有形式的drawImage()注意以下几点。 如果图像为null,或者水平或垂直尺寸为零,或者源高度或宽度为零,则drawImage()将引发异常。 如果图像无法被浏览器解码,或者在调用函数时尚未完成加载,则drawImage()将不显示任何内容。
这就是使用HTML5 canvas进行图像处理的全部内容。 现在让我们在实践中看一下。
<div id="slider">
<form>
<label>Easy</label>
<input type="range" id="scale" value="4" min="3" max="5" step="1">
<label>Hard</label>
</form>
<br>
</div>
<div id="main" class="main">
<canvas id="puzzle" width="480px" height="480px"></canvas>
</div>
该HTML块包括另一个HTML5功能,即范围输入,使用户可以使用滑块选择数字。 稍后我们将看到范围输入如何与拼图互动。 不过要预先警告:尽管大多数浏览器都支持范围输入,但是在撰写本文时,仍不支持其中两个最受欢迎的浏览器-Internet Explorer和Firefox。
现在,如上所述,要在画布上绘制,我们需要一个上下文。
var context = document.getElementById("puzzle").getContext("2d");
我们还将需要一张图片。 您可以使用下面引用的一个或适合画布(或可以调整大小以适合画布)的任何其他正方形图像。
var img = new Image();
img.src = 'http://www.brucealderman.info/Images/dimetrodon.jpg';
img.addEventListener('load', drawTiles, false);
事件监听器可以确保在浏览器尝试绘制图像之前完成图像的加载。 如果尚未准备好绘制画布,则画布将不会显示该图像。
我们将从拼图画布中获得木板的尺寸,并从范围输入中获得瓷砖的数量。 此滑块的范围是3到5,其中的数字值指示行和列的数量。
var boardSize = document.getElementById('puzzle').width;
var tileCount = document.getElementById('scale').value;
使用这两个数字,我们可以计算出图块大小。
var tileSize = boardSize / tileCount;
现在我们可以创建板了。
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;
}
setBoard()最后三个语句引入了我们尚未定义的变量。
我们需要跟踪空白图块的位置,并记录用户单击的位置。
var clickLoc = new Object;
clickLoc.x = 0;
clickLoc.y = 0;
var emptyLoc = new Object;
emptyLoc.x = 0;
emptyLoc.y = 0;
最终变量是一个布尔值,指示难题是否已解决。
var solved = false;
一旦所有拼图块都与各自的棋盘格匹配,我们就将其设置为true。
现在我们只需要与解决难题有关的功能。
首先,我们将设置由用户输入事件触发的功能。 如果更改了范围输入,则需要在重新绘制板之前重新计算图块的数量和大小。
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。实现起来比描述起来容易。
function distance(x1, y1, x2, y2) {
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
}
distance()函数通过获取x坐标之间的差的绝对值和y坐标之间的差的绝对值并相加来计算此距离。 如果此值为1,则可以将单击的图块移动到空心正方形中。 如果该值不是1,则不应移动图块。
要移动图块,我们只需将该板正方形的图块坐标复制到空正方形中即可。 然后将删除的图块的图块坐标复制到单击的图块中。
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);
}
}
}
}
绘制拼图块时,此功能可防止填充与emptyLoc坐标匹配的棋盘方块,直到设置了已解决的标志为止。 顺便说一句,由于每当移动范围滑块时板都会重新初始化,因此用户在解决难题后可以尝试另一个难度级别,而无需刷新页面。
这里的所有都是它的! canvas元素以及一些JavaScript和一些数学功能为HTML5带来了强大的本机图像操作。
















