拼图游戏:Trae 轻松实现图片拼接挑战

拼图游戏,无疑是经典的益智游戏之一。玩家需要将一张完整的图片分割成多个小块,然后通过拖动这些拼图块,拼接成原本的图案。这个游戏不仅考验玩家的观察力和空间想象力,还能带来无限的乐趣。

虽然拼图游戏的玩法看似简单,但要实现一个流畅的拖动拼接、检查拼图是否完成等功能,背后需要处理不少的逻辑和细节。过去,要实现拼图游戏,通常需要编写复杂的图片切割算法、拼图块的位置调整和碰撞检测等。

不过,Trae IDE 完全解决了这些问题。通过简单的指令,Trae 能自动帮你生成拼图游戏的规则和UI设计,让你轻松实现这一经典益智游戏。接下来,我就来分享如何通过 Trae 快速生成拼图游戏,让你轻松拥有一个既好玩又具挑战性的拼图游戏。

💡 我的需求其实很简单

我的需求非常明确:制作一个拼图游戏,功能要求如下:

  • 图片分割:将一张图片分割成多个小块,打乱它们的位置。
  • 拖动拼接:玩家可以通过拖动拼图块,将它们重新拼接成完整的图案。
  • 拼图完成判断:当所有拼图块正确排列时,游戏自动判定拼图完成。
  • 流畅的UI:游戏界面简洁,操作直观,拼图块拖动和拼接流畅自然。

虽然功能简单,但涉及到图片分割、拼图块的拖动逻辑和拼接完成判断,手动实现这些功能依然需要不少时间。

✨ Trae 如何理解需求并生成代码?

我只需要在 Trae 中输入一句话:

"生成一个拼图游戏,将图片分割成多个块,玩家拖动拼接成完整的图案。"

Trae 会自动解析并生成完整的拼图游戏代码,包括:

  • 图片分割:Trae 自动将上传的图片分割成多个小块,确保每个拼图块能够准确对应到原始图案。
  • 拼图块拖动逻辑:玩家可以通过鼠标拖动拼图块到空白位置,游戏自动处理拼图块的对齐与碰撞检测。
  • 拼图完成检测:当所有拼图块正确排列时,系统会检测到拼图完成,并给出提示。
  • 简洁的UI设计:拼图游戏界面简洁、直观,玩家可以轻松操作并享受游戏的乐趣。

通过这一简单的指令,Trae 就能够为我自动生成一个完整的拼图游戏,并且提供了非常流畅的用户体验。

🧩 游戏操作直观,拼图体验流畅

Trae 生成的拼图游戏操作非常简便,玩家只需点击并拖动拼图块,便能轻松地将其拖动到正确的位置。每当玩家将拼图块拖放到正确的地方,系统会自动更新拼图块的位置,直到所有拼图块正确组合成完整的图案。

整个游戏过程流畅自然,拼图块的拖动效果也十分平滑。玩家不需要任何复杂的操作,拼图完成的提示也非常及时,给玩家带来了极好的游戏体验。

🛠 游戏拓展,功能轻松加

虽然生成的拼图游戏已经非常完备,但 Trae 还支持轻松添加更多功能:

  • 难度选择:增加不同的难度等级,例如通过增加拼图块的数量,来增加拼图的挑战性。
  • 定时器:为游戏增加一个计时器,记录玩家完成拼图所用的时间,增强游戏的竞争性。
  • 提示功能:当玩家遇到困难时,可以提供提示功能,显示拼图的正确位置。
  • 音效和动画:为拼图完成时加入音效和动画,增加游戏的互动性和趣味性。

这些功能都可以通过简单的描述,Trae 就会自动为我生成相应的代码并集成到现有的游戏中。

这就是拼图游戏开发的新体验

通过这次拼图游戏的开发,我深刻体验到了 Trae 带来的便利。以前可能需要手动编写大量的代码,涉及到图片切割、拼图块的拖动以及完成判断等,现在只需要通过简单的指令,就能轻松实现这些功能。

对于独立开发者或小团队来说,Trae 不仅节省了开发时间,还能够提供流畅且富有创意的用户体验,让开发者专注于游戏设计而非复杂的代码编写。

结语

如果你也想制作一个拼图游戏,试试 Trae IDE,输入类似的需求:

"生成一个拼图游戏,将图片分割成多个块,玩家拖动拼接成完整的图案。"

几秒钟内,Trae 就会生成完整的拼图游戏代码,带有流畅的拼图块拖动逻辑和自动拼图完成检测。你可以直接将它嵌入到你的项目中,甚至根据需求继续扩展和优化。

快来体验一下 Trae,让你的拼图游戏开发变得更加轻松、有趣!

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>拼图游戏</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background-color: #f5f5f5;
            margin: 0;
            padding: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
            min-height: 100vh;
        }
        
        h1 {
            color: #333;
            margin-bottom: 20px;
            text-align: center;
        }
        
        .game-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            margin-bottom: 20px;
        }
        
        .puzzle-container {
            position: relative;
            border: 2px solid #333;
            margin-bottom: 20px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            background-color: #fff;
        }
        
        .puzzle-piece {
            position: absolute;
            cursor: move;
            box-sizing: border-box;
            transition: box-shadow 0.2s;
            background-size: cover;
            border: 1px solid #fff;
        }
        
        .puzzle-piece.dragging {
            z-index: 100;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
        }
        
        .puzzle-piece.correct {
            border: 1px solid #4CAF50;
        }
        
        .controls {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
            justify-content: center;
        }
        
        button {
            padding: 10px 15px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
        }
        
        button:hover {
            background-color: #45a049;
        }
        
        button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }
        
        .difficulty {
            margin-bottom: 20px;
        }
        
        .difficulty select {
            padding: 8px;
            font-size: 16px;
            border-radius: 4px;
        }
        
        .image-select {
            margin-bottom: 20px;
        }
        
        .image-select select {
            padding: 8px;
            font-size: 16px;
            border-radius: 4px;
        }
        
        .timer {
            font-size: 18px;
            margin-bottom: 10px;
            color: #333;
        }
        
        .moves {
            font-size: 18px;
            margin-bottom: 20px;
            color: #333;
        }
        
        .preview {
            margin-bottom: 20px;
            text-align: center;
        }
        
        .preview img {
            max-width: 200px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        
        .preview-toggle {
            margin-top: 10px;
        }
        
        .message {
            font-size: 20px;
            font-weight: bold;
            color: #4CAF50;
            height: 30px;
            text-align: center;
            margin-bottom: 10px;
        }
        
        .upload-container {
            margin-bottom: 20px;
            text-align: center;
        }
        
        .upload-container input {
            display: none;
        }
        
        .upload-label {
            padding: 10px 15px;
            background-color: #2196F3;
            color: white;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            display: inline-block;
            transition: background-color 0.3s;
        }
        
        .upload-label:hover {
            background-color: #0b7dda;
        }
        
        @media (max-width: 600px) {
            .puzzle-container {
                max-width: 90vw;
                max-height: 90vw;
            }
            
            .preview img {
                max-width: 150px;
            }
            
            .controls {
                flex-direction: column;
            }
        }
    </style>
</head>
<body>
    <h1>拼图游戏</h1>
    
    <div class="timer">时间: <span id="minutes">00</span>:<span id="seconds">00</span></div>
    <div class="moves">移动次数: <span id="move-count">0</span></div>
    
    <div class="controls">
        <div class="difficulty">
            <select id="difficulty">
                <option value="3">简单 (3x3)</option>
                <option value="4" selected>中等 (4x4)</option>
                <option value="5">困难 (5x5)</option>
                <option value="6">专家 (6x6)</option>
            </select>
        </div>
        
        <div class="image-select">
            <select id="image-select">
                <option value="nature">自然风景</option>
                <option value="city">城市景观</option>
                <option value="animal">可爱动物</option>
                <option value="food">美食</option>
            </select>
        </div>
        
        <button id="upload-btn" class="upload-label">上传图片</button>
        <input type="file" id="image-upload" accept="image/*">
    </div>
    
    <div class="preview">
        <h3>预览图</h3>
        <img id="preview-image" src="" alt="预览图">
        <div class="preview-toggle">
            <button id="toggle-preview">隐藏预览</button>
        </div>
    </div>
    
    <div class="message" id="message"></div>
    
    <div class="game-container">
        <div class="puzzle-container" id="puzzle-container"></div>
        
        <div class="controls">
            <button id="start-btn">开始游戏</button>
            <button id="shuffle-btn">重新打乱</button>
            <button id="solve-btn">显示解答</button>
        </div>
    </div>
    
    <script>
        // 游戏状态变量
        let puzzlePieces = [];
        let puzzleSize = 4; // 默认4x4
        let pieceWidth, pieceHeight;
        let puzzleWidth = 400;
        let puzzleHeight = 400;
        let draggedPiece = null;
        let startX, startY;
        let timer = null;
        let seconds = 0;
        let minutes = 0;
        let moveCount = 0;
        let gameStarted = false;
        let currentImage = null;
        let previewVisible = true;
        
        // 预定义的图片
        const images = {
            nature: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iIzg3Y2VlYiIvPjxwYXRoIGQ9Ik0wLDI1MEMyMCwyMzAgNDAsMjIwIDYwLDIzMEM4MCwyNDAgMTAwLDI1MCAxMjAsMjQwQzE0MCwyMzAgMTYwLDIyMCAxODAsMjMwQzIwMCwyNDAgMjIwLDI1MCAyNDAsMjQwQzI2MCwyMzAgMjgwLDIyMCAzMDAsMjMwQzMyMCwyNDAgMzQwLDI1MCAzNjAsMjQwQzM4MCwyMzAgNDAwLDIyMCA0MDAsMjIwVjQwMEgwWiIgZmlsbD0iIzFmNzc0MCIvPjxjaXJjbGUgY3g9IjMwMCIgY3k9IjgwIiByPSI0MCIgZmlsbD0iI2ZmZDcwMCIvPjxwYXRoIGQ9Ik0xMjAsMzAwQzEyMCwyNjAgMTYwLDI0MCAyMDAsMjQwQzI0MCwyNDAgMjgwLDI2MCAyODAsMzAwQzI4MCwzNDAgMjQwLDM2MCAyMDAsMzYwQzE2MCwzNjAgMTIwLDM0MCAxMjAsMzAwWiIgZmlsbD0iIzc3NTUzMyIvPjxwYXRoIGQ9Ik0xODAsMTgwQzE4MCwxNDAgMjIwLDEyMCAyNjAsMTIwQzMwMCwxMjAgMzQwLDE0MCAzNDAsMTgwQzM0MCwyMjAgMzAwLDI0MCAyNjAsMjQwQzIyMCwyNDAgMTgwLDIyMCAxODAsMTgwWiIgZmlsbD0iIzFmNzc0MCIvPjxwYXRoIGQ9Ik00MCwxNjBDNDAsMTIwIDgwLDEwMCAxMjAsMTAwQzE2MCwxMDAgMjAwLDEyMCAyMDAsMTYwQzIwMCwyMDAgMTYwLDIyMCAxMjAsMjIwQzgwLDIyMCA0MCwyMDAgNDAsMTYwWiIgZmlsbD0iIzFmNzc0MCIvPjwvc3ZnPg==',
            city: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iIzg3Y2VlYiIvPjxyZWN0IHg9IjAiIHk9IjI1MCIgd2lkdGg9IjQwMCIgaGVpZ2h0PSIxNTAiIGZpbGw9IiM4ODg4ODgiLz48cmVjdCB4PSIxMCIgeT0iMTUwIiB3aWR0aD0iODAiIGhlaWdodD0iMjUwIiBmaWxsPSIjNTU1NTU1Ii8+PHJlY3QgeD0iMTEwIiB5PSIxMDAiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMDAiIGZpbGw9IiM3Nzc3NzciLz48cmVjdCB4PSIyMTAiIHk9IjE4MCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjIyMCIgZmlsbD0iIzY2NjY2NiIvPjxyZWN0IHg9IjMxMCIgeT0iNzAiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMzAiIGZpbGw9IiM0NDQ0NDQiLz48cmVjdCB4PSIyMCIgeT0iMTcwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSI2MCIgeT0iMTcwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIyMCIgeT0iMjEwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSI2MCIgeT0iMjEwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIxMjAiIHk9IjEyMCIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiBmaWxsPSIjZmZmZjAwIi8+PHJlY3QgeD0iMTYwIiB5PSIxMjAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjEyMCIgeT0iMTYwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIxNjAiIHk9IjE2MCIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiBmaWxsPSIjZmZmZjAwIi8+PHJlY3QgeD0iMjIwIiB5PSIyMDAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjI2MCIgeT0iMjAwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIyMjAiIHk9IjI0MCIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiBmaWxsPSIjZmZmZjAwIi8+PHJlY3QgeD0iMjYwIiB5PSIyNDAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjMyMCIgeT0iOTAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjM2MCIgeT0iOTAiIHdpZHRoPSIxNSIgaGVpZ2h0PSIxNSIgZmlsbD0iI2ZmZmYwMCIvPjxyZWN0IHg9IjMyMCIgeT0iMTMwIiB3aWR0aD0iMTUiIGhlaWdodD0iMTUiIGZpbGw9IiNmZmZmMDAiLz48cmVjdCB4PSIzNjAiIHk9IjEzMCIgd2lkdGg9IjE1IiBoZWlnaHQ9IjE1IiBmaWxsPSIjZmZmZjAwIi8+PGNpcmNsZSBjeD0iMzAwIiBjeT0iNTAiIHI9IjMwIiBmaWxsPSIjZmZkNzAwIi8+PC9zdmc+',
            animal: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iI2FhZTZmZiIvPjxyZWN0IHg9IjAiIHk9IjMwMCIgd2lkdGg9IjQwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9IiM3N2RkNzciLz48Y2lyY2xlIGN4PSIyMDAiIGN5PSIyNTAiIHI9IjEwMCIgZmlsbD0iI2JiODg1NSIvPjxjaXJjbGUgY3g9IjE1MCIgY3k9IjIwMCIgcj0iMjUiIGZpbGw9IiNmZmZmZmYiLz48Y2lyY2xlIGN4PSIyNTAiIGN5PSIyMDAiIHI9IjI1IiBmaWxsPSIjZmZmZmZmIi8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iMjAwIiByPSIxMCIgZmlsbD0iIzAwMDAwMCIvPjxjaXJjbGUgY3g9IjI1MCIgY3k9IjIwMCIgcj0iMTAiIGZpbGw9IiMwMDAwMDAiLz48ZWxsaXBzZSBjeD0iMjAwIiBjeT0iMjUwIiByeD0iMzAiIHJ5PSIxNSIgZmlsbD0iI2ZmYWE5OSIvPjxwYXRoIGQ9Ik0xNTAsMTIwIEMxNTAsMTAwIDEyMCw4MCAxMDAsODAgQzgwLDgwIDUwLDEwMCA1MCwxMjAgQzUwLDE0MCA4MCwxNjAgMTAwLDE2MCBDMTIwLDE2MCAxNTAsMTQwIDE1MCwxMjAiIGZpbGw9IiNiYjg4NTUiLz48cGF0aCBkPSJNMjUwLDEyMCBDMjUwLDEwMCAyODAsODAgMzAwLDgwIEMzMjAsODAgMzUwLDEwMCAzNTAsMTIwIEMzNTAsMTQwIDMyMCwxNjAgMzAwLDE2MCBDMjgwLDE2MCAyNTAsMTQwIDI1MCwxMjAiIGZpbGw9IiNiYjg4NTUiLz48L3N2Zz4=',
            food: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgZmlsbD0iI2ZmZjVlNiIvPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjE1MCIgcj0iODAiIGZpbGw9IiNmZjZiNmIiLz48Y2lyY2xlIGN4PSIyODAiIGN5PSIxNTAiIHI9IjgwIiBmaWxsPSIjZmZjYzY2Ii8+PGNpcmNsZSBjeD0iMTIwIiBjeT0iMjgwIiByPSI4MCIgZmlsbD0iIzc3ZGQ3NyIvPjxjaXJjbGUgY3g9IjI4MCIgY3k9IjI4MCIgcj0iODAiIGZpbGw9IiM2NmNjZmYiLz48Y2lyY2xlIGN4PSIxMjAiIGN5PSIxNTAiIHI9IjYwIiBmaWxsPSIjZmYzMzMzIi8+PGNpcmNsZSBjeD0iMjgwIiBjeT0iMTUwIiByPSI2MCIgZmlsbD0iI2ZmYmIzMyIvPjxjaXJjbGUgY3g9IjEyMCIgY3k9IjI4MCIgcj0iNjAiIGZpbGw9IiM0NGFhNDQiLz48Y2lyY2xlIGN4PSIyODAiIGN5PSIyODAiIHI9IjYwIiBmaWxsPSIjMzM5OWZmIi8+PC9zdmc+'
        };
        
        // DOM 元素
        const puzzleContainer = document.getElementById('puzzle-container');
        const difficultySelect = document.getElementById('difficulty');
        const imageSelect = document.getElementById('image-select');
        const startBtn = document.getElementById('start-btn');
        const shuffleBtn = document.getElementById('shuffle-btn');
        const solveBtn = document.getElementById('solve-btn');
        const messageEl = document.getElementById('message');
        const minutesEl = document.getElementById('minutes');
        const secondsEl = document.getElementById('seconds');
        const moveCountEl = document.getElementById('move-count');
        const previewImage = document.getElementById('preview-image');
        const togglePreviewBtn = document.getElementById('toggle-preview');
        const imageUpload = document.getElementById('image-upload');
        const uploadBtn = document.getElementById('upload-btn');
        
        // 初始化游戏
        function initGame() {
            // 设置拼图容器大小
            puzzleContainer.style.width = puzzleWidth + 'px';
            puzzleContainer.style.height = puzzleHeight + 'px';
            
            // 清空拼图容器
            puzzleContainer.innerHTML = '';
            puzzlePieces = [];
            
            // 根据难度设置拼图大小
            puzzleSize = parseInt(difficultySelect.value);
            pieceWidth = puzzleWidth / puzzleSize;
            pieceHeight = puzzleHeight / puzzleSize;
            
            // 加载选中的图片
            loadImage();
        }
        
        // 加载图片
        function loadImage() {
            const selectedImage = imageSelect.value;
            currentImage = images[selectedImage];
            previewImage.src = currentImage;
            
            // 创建拼图块
            createPuzzlePieces();
        }
        
        // 创建拼图块
        function createPuzzlePieces() {
            puzzleContainer.innerHTML = '';
            puzzlePieces = [];
            
            for (let row = 0; row < puzzleSize; row++) {
                for (let col = 0; col < puzzleSize; col++) {
                    const piece = document.createElement('div');
                    piece.className = 'puzzle-piece';
                    piece.style.width = pieceWidth + 'px';
                    piece.style.height = pieceHeight + 'px';
                    
                    // 设置拼图块的背景图片位置
                    piece.style.backgroundImage = `url(${currentImage})`;
                    piece.style.backgroundSize = `${puzzleWidth}px ${puzzleHeight}px`;
                    piece.style.backgroundPosition = `-${col * pieceWidth}px -${row * pieceHeight}px`;
                    
                    // 设置拼图块的初始位置(有序)
                    piece.style.left = (col * pieceWidth) + 'px';
                    piece.style.top = (row * pieceHeight) + 'px';
                    
                    // 存储拼图块的正确位置
                    piece.dataset.row = row;
                    piece.dataset.col = col;
                    piece.dataset.correctLeft = col * pieceWidth;
                    piece.dataset.correctTop = row * pieceHeight;
                    
                    // 添加拖拽事件
                    piece.addEventListener('mousedown', startDrag);
                    piece.addEventListener('touchstart', startDrag, { passive: false });
                    
                    puzzleContainer.appendChild(piece);
                    puzzlePieces.push(piece);
                }
            }
        }
        
        // 打乱拼图
        function shufflePuzzle() {
            if (!gameStarted) return;
            
            // 重置移动次数
            moveCount = 0;
            moveCountEl.textContent = moveCount;
            
            // 随机排列拼图块
            const positions = [];
            for (let row = 0; row < puzzleSize; row++) {
                for (let col = 0; col < puzzleSize; col++) {
                    positions.push({ left: col * pieceWidth, top: row * pieceHeight });
                }
            }
            
            // 打乱位置数组
            shuffleArray(positions);
            
            // 应用打乱后的位置
            for (let i = 0; i < puzzlePieces.length; i++) {
                const piece = puzzlePieces[i];
                const position = positions[i];
                
                piece.style.left = position.left + 'px';
                piece.style.top = position.top + 'px';
                
                // 移除正确位置标记
                piece.classList.remove('correct');
            }
            
            // 检查是否已经完成(防止随机排列恰好是正确的)
            checkCompletion();
        }
        
        // 打乱数组(Fisher-Yates 洗牌算法)
        function shuffleArray(array) {
            for (let i = array.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [array[i], array[j]] = [array[j], array[i]];
            }
            return array;
        }
        
        // 开始拖拽
        function startDrag(e) {
            if (!gameStarted) return;
            
            e.preventDefault();
            
            // 获取触摸或鼠标事件的坐标
            const clientX = e.clientX || e.touches[0].clientX;
            const clientY = e.clientY || e.touches[0].clientY;
            
            draggedPiece = this;
            draggedPiece.classList.add('dragging');
            
            // 计算鼠标在拼图块内的位置
            const rect = draggedPiece.getBoundingClientRect();
            startX = clientX - rect.left;
            startY = clientY - rect.top;
            
            // 添加移动和结束拖拽事件
            document.addEventListener('mousemove', dragMove);
            document.addEventListener('mouseup', dragEnd);
            document.addEventListener('touchmove', dragMove, { passive: false });
            document.addEventListener('touchend', dragEnd);
        }
        
        // 拖拽移动
        function dragMove(e) {
            if (!draggedPiece) return;
            
            e.preventDefault();
            
            // 获取触摸或鼠标事件的坐标
            const clientX = e.clientX || e.touches[0].clientX;
            const clientY = e.clientY || e.touches[0].clientY;
            
            // 计算新位置
            const puzzleRect = puzzleContainer.getBoundingClientRect();
            let left = clientX - puzzleRect.left - startX;
            let top = clientY - puzzleRect.top - startY;
            
            // 限制在拼图容器内
            left = Math.max(0, Math.min(left, puzzleWidth - pieceWidth));
            top = Math.max(0, Math.min(top, puzzleHeight - pieceHeight));
            
            // 更新位置
            draggedPiece.style.left = left + 'px';
            draggedPiece.style.top = top + 'px';
        }
        
        // 结束拖拽
        function dragEnd() {
            if (!draggedPiece) return;
            
            // 移除拖拽中的样式
            draggedPiece.classList.remove('dragging');
            
            // 吸附到网格
            snapToGrid(draggedPiece);
            
            // 增加移动次数
            moveCount++;
            moveCountEl.textContent = moveCount;
            
            // 检查是否完成拼图
            checkCompletion();
            
            // 清除拖拽状态
            draggedPiece = null;
            
            // 移除事件监听器
            document.removeEventListener('mousemove', dragMove);
            document.removeEventListener('mouseup', dragEnd);
            document.removeEventListener('touchmove', dragMove);
            document.removeEventListener('touchend', dragEnd);
        }
        
        // 吸附到网格
        function snapToGrid(piece) {
            const left = parseInt(piece.style.left);
            const top = parseInt(piece.style.top);
            
            // 计算最近的网格位置
            const col = Math.round(left / pieceWidth);
            const row = Math.round(top / pieceHeight);
            
            // 计算吸附位置
            const snapLeft = col * pieceWidth;
            const snapTop = row * pieceHeight;
            
            // 设置吸附位置
            piece.style.left = snapLeft + 'px';
            piece.style.top = snapTop + 'px';
            
            // 检查是否在正确位置
            const correctLeft = parseInt(piece.dataset.correctLeft);
            const correctTop = parseInt(piece.dataset.correctTop);
            
            if (snapLeft === correctLeft && snapTop === correctTop) {
                piece.classList.add('correct');
            } else {
                piece.classList.remove('correct');
            }
        }
        
        // 检查是否完成拼图
        function checkCompletion() {
            let completed = true;
            
            for (const piece of puzzlePieces) {
                const currentLeft = parseInt(piece.style.left);
                const currentTop = parseInt(piece.style.top);
                const correctLeft = parseInt(piece.dataset.correctLeft);
                const correctTop = parseInt(piece.dataset.correctTop);
                
                if (currentLeft !== correctLeft || currentTop !== correctTop) {
                    completed = false;
                    break;
                }
            }
            
            if (completed && gameStarted) {
                // 停止计时器
                stopTimer();
                
                // 显示完成消息
                messageEl.textContent = `恭喜!你完成了拼图!用时 ${minutes}:${seconds.toString().padStart(2, '0')},移动 ${moveCount} 次`;
                
                // 禁用重新打乱按钮
                shuffleBtn.disabled = true;
            }
        }
        
        // 显示解答
        function showSolution() {
            if (!gameStarted) return;
            
            // 将每个拼图块放回正确位置
            for (const piece of puzzlePieces) {
                const correctLeft = parseInt(piece.dataset.correctLeft);
                const correctTop = parseInt(piece.dataset.correctTop);
                
                piece.style.left = correctLeft + 'px';
                piece.style.top = correctTop + 'px';
                piece.classList.add('correct');
            }
            
            // 停止计时器
            stopTimer();
            
            // 显示消息
            messageEl.textContent = '这是拼图的解答。';
            
            // 禁用按钮
            shuffleBtn.disabled = true;
            solveBtn.disabled = true;
        }
        
        // 计时器函数
        function startTimer() {
            stopTimer();
            seconds = 0;
            minutes = 0;
            updateTimer();
            
            timer = setInterval(() => {
                seconds++;
                if (seconds === 60) {
                    seconds = 0;
                    minutes++;
                }
                updateTimer();
            }, 1000);
        }
        
        function stopTimer() {
            clearInterval(timer);
        }
        
        function updateTimer() {
            minutesEl.textContent = minutes.toString().padStart(2, '0');
            secondsEl.textContent = seconds.toString().padStart(2, '0');
        }
        
        // 开始新游戏
        function startGame() {
            // 初始化游戏
            initGame();
            
            // 开始计时
            startTimer();
            
            // 重置移动次数
            moveCount = 0;
            moveCountEl.textContent = moveCount;
            
            // 清除消息
            messageEl.textContent = '';
            
            // 启用按钮
            shuffleBtn.disabled = false;
            solveBtn.disabled = false;
            
            // 设置游戏状态
            gameStarted = true;
            
            // 打乱拼图
            shufflePuzzle();
        }
        
        // 处理图片上传
        function handleImageUpload(e) {
            const file = e.target.files[0];
            if (!file) return;
            
            // 检查文件类型
            if (!file.type.match('image.*')) {
                alert('请上传图片文件!');
                return;
            }
            
            const reader = new FileReader();
            reader.onload = function(event) {
                // 创建图像对象以获取尺寸
                const img = new Image();
                img.onload = function() {
                    // 更新当前图像
                    currentImage = event.target.result;
                    previewImage.src = currentImage;
                    
                    // 如果游戏已经开始,重新开始游戏
                    if (gameStarted) {
                        startGame();
                    } else {
                        initGame();
                    }
                };
                img.src = event.target.result;
            };
            reader.readAsDataURL(file);
        }
        
        // 切换预览图显示/隐藏
        function togglePreview() {
            previewVisible = !previewVisible;
            previewImage.style.display = previewVisible ? 'block' : 'none';
            togglePreviewBtn.textContent = previewVisible ? '隐藏预览' : '显示预览';
        }
        
        // 事件监听器
        startBtn.addEventListener('click', startGame);
        shuffleBtn.addEventListener('click', shufflePuzzle);
        solveBtn.addEventListener('click', showSolution);
        difficultySelect.addEventListener('change', initGame);
        imageSelect.addEventListener('change', loadImage);
        togglePreviewBtn.addEventListener('click', togglePreview);
        imageUpload.addEventListener('change', handleImageUpload);
        uploadBtn.addEventListener('click', () => imageUpload.click());
        
        // 窗口大小调整处理
        window.addEventListener('resize', function() {
            // 在小屏幕上调整拼图大小
            if (window.innerWidth < 600) {
                puzzleWidth = Math.min(window.innerWidth - 40, 400);
                puzzleHeight = puzzleWidth; // 保持正方形
                
                // 更新拼图容器大小
                puzzleContainer.style.width = puzzleWidth + 'px';
                puzzleContainer.style.height = puzzleHeight + 'px';
                
                // 更新拼图块大小和位置
                pieceWidth = puzzleWidth / puzzleSize;
                pieceHeight = puzzleHeight / puzzleSize;
                
                for (let i = 0; i < puzzlePieces.length; i++) {
                    const piece = puzzlePieces[i];
                    const row = parseInt(piece.dataset.row);
                    const col = parseInt(piece.dataset.col);
                    
                    // 更新拼图块大小
                    piece.style.width = pieceWidth + 'px';
                    piece.style.height = pieceHeight + 'px';
                    
                    // 更新背景大小和位置
                    piece.style.backgroundSize = `${puzzleWidth}px ${puzzleHeight}px`;
                    piece.style.backgroundPosition = `-${col * pieceWidth}px -${row * pieceHeight}px`;
                    
                    // 更新正确位置数据
                    piece.dataset.correctLeft = col * pieceWidth;
                    piece.dataset.correctTop = row * pieceHeight;
                    
                    // 如果游戏未开始,更新当前位置
                    if (!gameStarted) {
                        piece.style.left = col * pieceWidth + 'px';
                        piece.style.top = row * pieceHeight + 'px';
                    }
                }
            }
        });
        
        // 初始化游戏
        window.onload = function() {
            initGame();
            shuffleBtn.disabled = true;
            solveBtn.disabled = true;
        };
    </script>
</body>
</html>
相关推荐
前端日常开发1 小时前
七夕快到了,看看Trae老师怎么实现浪漫的烟花
trae
前端日常开发1 小时前
记忆卡牌,锻炼你的瞬间记忆力,让Trae帮你完成这个小游戏的开发
trae
zhuyasen2 小时前
当Go框架拥有“大脑”,Sponge框架集成AI开发项目,从“手写”到一键“生成”业务逻辑代码
后端·go·ai编程
萌萌哒草头将军3 小时前
🚀🚀🚀 告别复制粘贴,这个高效的 Vite 插件让我摸鱼🐟时间更充足了!
前端·vite·trae
TimelessHaze4 小时前
【performance面试考点】让面试官眼前一亮的performance性能优化
前端·性能优化·trae
dremtri6 小时前
ECharts雷达图自定义数据点位置
echarts·trae
bug菌6 小时前
还在为Java API文档熬夜加班?字节Trae让你躺着就能生成专业文档!
aigc·ai编程·trae
AI大模型6 小时前
30天快速入门AI大模型:从理论到实践的详细学习方案
程序员·llm·ai编程