JSFiddle - React, Tailwind, and code Playground
CSS
.puzzle {
display: inline-block;
margin: -8px;
}
JavaScript
const src = "https://i.imgur.com/r0LPrzk.png";
class Puzzle {
constructor({
src,
rows,
columns,
width,
height
}) {
this.src = src;
this.rows = rows;
this.columns = columns;
this.width = width;
this.height = height;
this.tileWidth = width / columns;
this.tileHeight = height / rows;
this.tiles = Array.from({
length: rows * columns
}).map((_, i) => {
const x = i % columns;
const y = Math.floor(i / columns);
return {
x,
y,
imageX: x,
imageY: y,
empty: false
};
});
this.emptyTile = last(this.tiles);
this.emptyTile.empty = true;
this.scramble();
this.createElements();
}
slideTile(tile) {
const {
emptyTile
} = this;
if (!this.areTilesNeighbors(tile, emptyTile)) return;
this.swapTiles(tile, emptyTile);
this.updateTileElPosition(tile);
this.updateTileElPosition(emptyTile);
}
swapTiles(a, b) {
const {
x: ax,
y: ay
} = a;
const {
x: bx,
y: by
} = b;
a.x = bx;
a.y = by;
b.x = ax;
b.y = ay;
}
scramble() {
const {
emptyTile
} = this;
let last = null;
const isValidTile = (tile) => tile !== last && tile !== emptyTile && this.areTilesNeighbors(tile, emptyTile);
for (let i = 0; i < 1000; i++) {
const nextTile = last = sample(this.tiles.filter(isValidTile));
this.swapTiles(emptyTile, nextTile);
}
}
updateTileElPosition(tile) {
const {
tileWidth,
tileHeight
} = this;
const {
el,
x,
y
} = tile;
el.style.left = `${x * tileWidth}px`;
el.style.top = `${y * tileHeight}px`;
}
areTilesNeighbors(a, b) {
const {
x: ax,
y: ay
} = a;
const {
x: bx,
y: by
} = b;
return (
(a !== b) &&
(
(ax === bx && (ay === by + 1 || ay === by - 1)) ||
(ay === by && (ax === bx + 1 || ax...