简体   繁体   中英

Sliding puzzle (javascript & HTML5) - need to animate moving elements

I've got a sliding puzzle where an image is split into a grid and you can move squares into an empty space. I'd like for this to animate, so to actually slide into the empty space, not just appear there. Here's the codepen I'm working from: http://codepen.io/akshivictor/pen/jPPypW and the javascript below.

var context = document.getElementById('puzzle').getContext('2d');

var img = new Image();
img.src = 'http://i.imgur.com/H1la3ZG.png?1';
img.addEventListener('load', drawTiles, false);

var boardSize = document.getElementById('puzzle').width;
var tileCount =3;

var tileSize = boardSize / tileCount;

var clickLoc = new Object;
clickLoc.x = 0;
clickLoc.y = 0;

var emptyLoc = new Object;
emptyLoc.x = 0;
emptyLoc.y = 0;

var solved = false;

var boardParts;
setBoard();

document.getElementById('scale').onchange = function() {
  tileCount = this.value;
  tileSize = boardSize / tileCount;
  setBoard();
  drawTiles();
};

document.getElementById('puzzle').onclick = function(e) {
  clickLoc.x = Math.floor((e.pageX - this.offsetLeft) / tileSize);
  clickLoc.y = Math.floor((e.pageY - this.offsetTop) / tileSize);
  if (distance(clickLoc.x, clickLoc.y, emptyLoc.x, emptyLoc.y) == 1) {
    slideTile(emptyLoc, clickLoc);
    drawTiles();
  }
};

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;
}

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);
      }
    }
  }
}

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;
}

I've made adaptations from the sliding puzzle in this article http://www.sitepoint.com/image-manipulation-with-html5-canvas-a-sliding-puzzle-2/

The general way to do translational animation is :

animate(object):
    while(object.not_in_place())
        object.move(small_amount)
        sleep(some_time)
        draw(object)
    repeat

Hence, move your images a small amount (few pixels) in the right direction and repeat that movement fast enough (though with sleeps in between) to get a sliding animation.

In case you need ease-in or ease-out animations, your translation amount will change with the duration.

For repeated calls after a fixed interval, use setTimeout in JavaScript.

More Details

Lets say you are moving from (a, b) to (c, b) (which are coordinates in 2 dimensional Cartesian plane), then the distance moved in x-direction is (c - b) and y-direction is 0 . So, divide the distance (c - b) in equal amounts to say x .

Let the total duration of the animation be T , then divide that into same number of intervals say t . Hence, your algorithms becomes:

while(object.x != c)
    object.x += x
    sleep(t)
    object.draw()
repeat 

Similarly, you can do for the y-direction case. Note that in your game you dont need to move in any arbitrary direction, only either horizontally or vertically which simplifies the implementation.

While, JavaScript has no equivalent sleep() routine, you can use setTimeout like this :

function x() { 
    id = setTimeout(x, 1000); // x will be called every 1 seconds ( = 1000 ms)
}

clearTimeout(id); // this removes the timeout and stops the repeated function calls

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM