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带来了强大的本机图像操作。