当前位置:首页 > 行业动态 > 正文

html拼图游戏制作

使用HTML布局,CSS样式,JavaScript实现图片切割、打乱排序、拖拽逻辑及碰撞检测,通过坐标匹配判断拼图成功,添加动画

拼图游戏结构设计

组件 说明
容器元素 <div id="puzzle-container"> 用于包裹所有拼图块
拼图块 多个<div class="puzzle-piece"> 通过CSS排列成网格
图片分割 使用CSS background-position 或 JavaScript 切割原图为等分小块
空白块 预留一个位置作为临时空白区域,用于拼图块交换位置

核心功能实现步骤

HTML基础结构

<div id="puzzle-container">
  <div class="puzzle-piece" data-index="0"></div>
  <div class="puzzle-piece" data-index="1"></div>
  <!-其他拼图块 -->
  <div class="empty-space"></div> <!-空白占位符 -->
</div>

CSS样式布局

#puzzle-container {
  width: 300px;
  height: 300px;
  position: relative;
  border: 1px solid #ccc;
}
.puzzle-piece {
  width: 100px;
  height: 100px;
  position: absolute;
  background-size: 300px 300px; / 假设原图尺寸为300x300 /
}
.puzzle-piece[data-index="0"] { background-position: 0 0; }
.puzzle-piece[data-index="1"] { background-position: -100px 0; }
/ 其他块的背景定位规则 /
.empty-space {
  width: 100px;
  height: 100px;
  background-color: #eee;
}

JavaScript交互逻辑

const container = document.getElementById('puzzle-container');
let currentPiece = null; // 当前拖动的拼图块
let offsetX, offsetY;    // 鼠标偏移量
// 初始化拖拽事件
container.addEventListener('mousedown', function(e) {
  if (e.target.classList.contains('puzzle-piece')) {
    currentPiece = e.target;
    const rect = currentPiece.getBoundingClientRect();
    offsetX = e.clientX rect.left;
    offsetY = e.clientY rect.top;
    document.onmousemove = dragMove;
    document.onmouseup = drop;
  }
});
function dragMove(e) {
  currentPiece.style.position = 'absolute';
  currentPiece.style.left = `${e.clientX offsetX}px`;
  currentPiece.style.top = `${e.clientY offsetY}px`;
}
function drop(e) {
  document.onmousemove = null;
  document.onmouseup = null;
  const target = document.elementFromPoint(e.clientX, e.clientY);
  if (target && target.classList.contains('puzzle-piece')) {
    swapPosition(currentPiece, target);
    checkWin(); // 每次交换后检查是否完成
  } else {
    currentPiece.style.position = ''; // 恢复原定位
    currentPiece.style.left = '';
    currentPiece.style.top = '';
  }
}
function swapPosition(a, b) {
  const aRect = a.getBoundingClientRect();
  const bRect = b.getBoundingClientRect();
  a.style.left = `${bRect.left}px`;
  a.style.top = `${bRect.top}px`;
  b.style.left = `${aRect.left}px`;
  b.style.top = `${aRect.top}px`;
}

胜利条件检测

function checkWin() {
  const pieces = document.querySelectorAll('.puzzle-piece');
  let isWin = true;
  pieces.forEach(piece => {
    const index = parseInt(piece.getAttribute('data-index'));
    const expectedTop = Math.floor(index / 3)  100; // 3x3网格计算
    const expectedLeft = (index % 3)  100;
    if (parseInt(piece.style.top) !== expectedTop || parseInt(piece.style.left) !== expectedLeft) {
      isWin = false;
    }
  });
  if (isWin) {
    alert('拼图完成!');
  }
}

相关问题与解答

问题1:如何动态切割任意尺寸的图片?
解答:

html拼图游戏制作  第1张

  1. 使用<img>标签预加载图片,获取其宽高。
  2. 根据行数和列数计算每块的宽度和高度。
  3. 通过JavaScript动态生成拼图块,设置background-image为原图,调整background-position实现切割。
    示例代码:

    const originalImage = document.createElement('img');
    originalImage.src = 'path/to/image.jpg';
    originalImage.onload = function() {
    const rows = 3, cols = 3;
    const pieceWidth = originalImage.width / cols;
    const pieceHeight = originalImage.height / rows;
    for (let i = 0; i < rows  cols; i++) {
     const piece = document.createElement('div');
     piece.classList.add('puzzle-piece');
     piece.style.backgroundImage = `url(${originalImage.src})`;
     piece.style.backgroundSize = `${originalImage.width}px ${originalImage.height}px`;
     piece.style.backgroundPosition = `${-(i % cols)  pieceWidth}px ${-Math.floor(i / cols)  pieceHeight}px`;
     piece.setAttribute('data-index', i);
     container.appendChild(piece);
    }
    };

问题2:如何限制拼图块只能与相邻块交换?
解答:

  1. drop事件中,计算当前块与目标块的索引差值。
  2. 仅允许索引差值为1(横向相邻)或3(纵向相邻,3×3网格时)的块交换。
    示例代码:

    function drop(e) {
    // ...原有代码...
    const currentIndex = parseInt(currentPiece.getAttribute('data-index'));
    const targetIndex = parseInt(target.getAttribute('data-index'));
    const isAdjacent = Math.abs(currentIndex targetIndex) === 1 || Math.abs(currentIndex targetIndex) === 3;
    if (isAdjacent) {
     swapPosition(currentPiece, target);
     checkWin();
    } else {
     currentPiece.style.position = ''; // 恢复原定位
     currentPiece.style.left = '';
     currentPiece.style.top = '';
    }
    }
0