Статьи

Управление изображениями с помощью HTML5 Canvas: скользящая головоломка

HTML5 включает в себя множество функций для естественной интеграции мультимедиа в веб-страницы. Среди этих функций — элемент canvas, чистый лист, который можно заполнить линейными рисунками, файлами изображений или анимацией.

В этом уроке я собираюсь продемонстрировать возможности манипулирования изображениями на холсте HTML5, создав скользящую головоломку.

Чтобы встроить холст в веб-страницу, используйте <canvas>

  <canvas width = "480px" height = "480px"> </ canvas> 

Атрибуты width и height устанавливают размер холста в пикселях. Если эти атрибуты не указаны, по умолчанию они имеют ширину 300 пикселей и высоту 150 пикселей.

Рисование на холсте выполняется через контекст, который инициализируется с помощью функции JavaScript getContext() Двумерный контекст, заданный W3C, называется, соответственно, «2d». Итак, чтобы инициализировать контекст для холста с идентификатором «canvas», мы просто вызываем:

  document.getElementById ( "холст") getContext ( "2d"). 

Следующим шагом является отображение изображения. В JavaScript есть только одна функция — drawImage() В своей основной форме эта функция принимает три аргумента: объект изображения и смещение по осям x и y от верхнего левого угла холста.

  drawImage (изображение, х, у); 

Также можно добавить еще два аргумента, ширину и высоту, чтобы изменить размер изображения.

  drawImage (изображение, x, y, ширина, высота); 

Наиболее сложная форма drawImage() Первый — это объект изображения. Следующие четыре по порядку: источник x, y, ширина и высота. Остальные четыре — по порядку, пункт назначения x, y, ширина и высота. Эта функция извлекает часть изображения для рисования на холсте, изменяя его размер при необходимости. Это позволяет нам рассматривать изображение как спрайт.

  drawImage (изображение, sx, sy, sw, sh, dx, dy, dw, dh); 

Несколько предостережений в порядке со всеми формами drawImage() Если изображение равно нулю, или горизонтальное или вертикальное измерение равно нулю, или высота или ширина источника равна нулю, drawImage() Если изображение не может быть декодировано браузером или не завершило загрузку при вызове функции, drawImage()

Вот и все, что нужно для манипулирования изображениями с помощью HTML5 canvas. Теперь посмотрим на практике.

  <div id = "slider">
     <Форма>
       <Метка> Легко </ метка>
       <input type = "range" id = "scale" value = "4" min = "3" max = "5" step = "1">
       <Метка> Hard </ метка>
     </ Форма>
     <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 ("пазл"). getContext ("2d"); 

Нам также понадобится изображение. Вы можете использовать изображение, указанное ниже, или любое другое квадратное изображение, которое подходит (или может быть изменено по размеру) холсту.

  var img = новое изображение ();
   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 = новый объект;
   setBoard (); 

Функция setBoard() Естественный способ представления доски — двумерный массив. В JavaScript создание такого массива не элегантный процесс. Сначала мы объявляем плоский массив, затем объявляем каждый из элементов массива как массив. Эти элементы могут быть доступны, как если бы они были многомерным массивом.

В скользящей игре-головоломке каждый элемент будет объектом с координатами x и y, которые определяют его местоположение в сетке головоломки. Поэтому каждый объект будет иметь два набора координат. Первым будет его позиция в массиве. Это представляет его местоположение на доске, поэтому я буду называть это квадратом доски. Каждый квадрат доски имеет объект со свойствами x и y, которые представляют его местоположение на изображении головоломки. Я буду называть это место плиткой головоломки. Когда координаты квадрата доски совпадают с координатами его мозаичного элемента мозаики, мозаика находится в правильном месте для решения головоломки.

Для целей этого урока мы инициализируем каждую клетку головоломки в квадрат доски напротив ее правильного положения в головоломке. Например, плитка для правого верхнего угла будет находиться на площади доски в левом нижнем углу.

  function setBoard () {
     boardParts = новый массив (tileCount);
     for (var i = 0; i <tileCount; ++ i) {
       boardParts [i] = new Array (tileCount);
       для (var j = 0; j <tileCount; ++ j) {
         boardParts [i] [j] = новый объект;
         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;
     решено = ложно;
   } 

Эти последние три оператора в setBoard()

Нам нужно будет отслеживать местоположение пустой плитки и записывать, где пользователь нажимает.

  var clickLoc = новый объект;
   clickLoc.x = 0;
   clickLoc.y = 0;

   var emptyLoc = новый объект;
   emptyLoc.x = 0;
   emptyLoc.y = 0; 

Последняя переменная — логическое значение, указывающее, была ли загадка решена.

  переменная решена = ложь; 

Мы установим это на 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 ();
     }
     если (решено) {
       alert («Вы решили это!»);
     }
   }; 

В некоторых браузерах разрешенное оповещение может сработать до того, как доска закончит перерисовку. Чтобы предотвратить это, дайте предупреждение короткую задержку.

  если (решено) {
     setTimeout (function () {alert («Вы решили!»);}, 500);
   } 

При нажатии на плитку мы должны знать, находится ли она рядом с открытым квадратом. Это верно тогда и только тогда, когда общее расстояние от нажатой плитки до открытого квадрата равно 1, другими словами, если разница x-координат выбранной плитки и пустой плитки плюс разница y-координат выбранная плитка и пустая плитка — 1. Это легче реализовать, чем описать.

  функциональное расстояние (x1, y1, x2, y2) {
     вернуть Math.abs (x1 - x2) + Math.abs (y1 - y2);
   } 

Функция distance() Если это значение равно 1, клик по плитке можно переместить в открытый квадрат. Если это значение отличается от 1, плитку не следует перемещать.

Чтобы переместить плитку, мы просто копируем координаты плитки для этого квадрата доски в пустой квадрат. Затем скопируйте координаты плитки для удаленной плитки в выбранную плитку.

  function slideTile (toLoc, fromLoc) {
     если (! решено) {
       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) {
       для (var j = 0; j <tileCount; ++ j) {
         if (boardParts [i] [j] .x! = i || boardParts [i] [j] .y! = j) {
           флаг = ложь;
         }
       }
     }
     решено = флаг;
   } 

Если какие-либо плитки не на своем месте, функция возвращает false. В противном случае по умолчанию это правда.

Наконец, перерисуйте доску, нажав плитку в новом положении.

  function drawTiles () {
     context.clearRect (0, 0, boardSize, boardSize);
     for (var i = 0; i <tileCount; ++ i) {
       для (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 || решено == правда) {
           context.drawImage (img, x * tileSize, y * tileSize, tileSize, tileSize,
               i * tileSize, j * tileSize, tileSize, tileSize);
         }
       }
     }
   } 

При рисовании тайлов головоломки эта функция предотвращает заполнение квадрата доски, который соответствует координатам emptyLoc, пока не будет установлен флаг решен. Кстати, поскольку доска переинициализируется всякий раз, когда перемещается ползунок диапазона, пользователь может попробовать другой уровень сложности после решения головоломки без обновления страницы.

Это все, что нужно сделать! Элемент canvas вместе с небольшим JavaScript и небольшой математикой привносит мощные манипуляции с собственными изображениями в HTML5.

Вы найдете живую демонстрацию скользящей головоломки на http://html5.brucealderman.info/sliding.html .