前端技术搭建拼图小游戏(内含源码)

news2024/11/24 2:10:59

The sand accumulates to form a pagoda

  • ✨ 写在前面
  • ✨ 功能介绍
  • ✨ 页面搭建
  • ✨ 样式设置
  • ✨ 逻辑部分


✨ 写在前面

上周我们实通过前端基础实现了俄罗斯方块游戏,今天还是继续按照我们原定的节奏来带领大家完成一个拼图游戏,功能也比较简单简单,也是想借助这样一个简单的功能,然后来帮助大家了解我们JavaScript在前端中的作用, 在前面的文章当中我们也提及到我们在本系列的专栏是循序渐进从简单到复杂的过程,这份专栏中我们会带领大家用前端实现翻卡片、飞机大战、弹珠游戏、贪吃蛇、井字游戏、拼图、连连看、扫雷等等有趣的小游戏,纯前端语言实现,都会陆续带给大家。欢迎大家订阅我们这份前端小游戏的专栏。订阅链接:https://blog.csdn.net/jhxl_/category_12261013.html


✨ 功能介绍

拼图游戏是一种经典的益智游戏,目标是将一张图片切割成多个小块,然后通过拖动小块的位置,重新排列它们以恢复原始图片。这个游戏测试你的观察能力、空间认知能力和逻辑思维能力。它不仅有助于放松大脑,还提供了娱乐和挑战。

功能介绍:

  • 图片切割:游戏开始时,一张图片会被切割成多个小块,并以随机顺序排列在游戏区域内。
  • 拖拽移动:通过鼠标点击小块并拖拽,可以移动小块的位置。
  • 打乱拼图:点击"打乱拼图"按钮,可以重新随机排列拼图块的位置,增加挑战和乐趣。
  • 恢复拼图:玩家的目标是将拼图块按照正确的顺序重新排列,恢复原始图片。

玩法说明:

  1. 游戏开始时,一张图片被切割成多个小块并随机排列在游戏区域内。
  2. 使用鼠标点击一个小块,然后拖拽它的位置,将其移动到空白位置或与其他小块交换位置。
  3. 继续移动其他小块,逐渐调整它们的位置,以恢复原始图片的完整性。
  4. 如果感到困惑或遇到难题,可以点击"打乱拼图"按钮重新排列小块,重新开始。
  5. 当所有小块都恰当地拼合在一起,恢复成原始图片时,游戏胜利。
  6. 在完成拼图后,会弹出恭喜信息的提示框,可以选择重新开始游戏或继续挑战。

在这里插入图片描述

这个拼图游戏结合了视觉和操作的挑战,你可以尝试不同的策略和方法来完成拼图,挑战自己的速度和技巧,享受拼图的乐趣和成就感。玩这个游戏,锻炼你的大脑,放松身心!


✨ 页面搭建

创建文件

首先呢我们创建我们的HTML文件,这里我就直接命名为 拼图.html 了,大家可以随意命名, 文件创建生成后我们通过编辑器打开,这里我用的是VScode, 然后初始化我们的代码结构,那在这里告诉大家一个快捷键,就是我们敲上我们英文的一个 ! 我们敲击回车直接就会给我们生成基础版本的前端代码结构。

在这里插入图片描述

文档声明和编码设置: 在HTML文档的头部,使用<!DOCTYPE>声明HTML文档类型,确保浏览器以正确的方式渲染网页内容。同时,设置UTF-8编码,以确保浏览器能够正确地解析和显示中文字符。下面我就开始搭建我们的DOM结构了!

DOM结构搭建

以下是拼图游戏的基本DOM结构。玩家将在游戏板上看到拼图块,并通过点击按钮来打乱拼图块的位置。你可以根据这个基本结构来进一步实现拼图游戏的逻辑和功能。

<body>
    <h2>图片切换拼图</h2>
    <div id="puzzle-container">
        <div id="puzzle-board"></div>
    </div>
    <button id="shuffle-btn">打乱拼图</button>
</body>
  • <h2>图片切换拼图</h2>:这是一个标题,用于显示游戏的名称或标题,让玩家知道这是一个拼图游戏。

  • <div id="puzzle-container">:这是一个容器,用于包含拼图游戏的主要内容。拼图块将显示在这个容器内。

  • <div id="puzzle-board"></div>:这是一个用于显示拼图块的游戏板。在游戏开始时,拼图块会被放置在这个游戏板上。

  • <button id="shuffle-btn">打乱拼图</button>:这是一个按钮,当点击它时,会触发打乱拼图的功能。点击这个按钮后,拼图块的位置将被重新随机排列,增加游戏的挑战性。

在这里插入图片描述


✨ 样式设置

我们看到了上面的的DOM已经搭建好了,但是页面什么都看不出来,下面我们简单的来配置一下样式吧,其实我们本专栏也是想带领大家掌握一些逻辑所以样式方面我们就一切从简;

<style>
    #puzzle-container {
        width: 300px;
        height: 300px;
        margin: 0 auto;
        border: 2px solid #ccc;
        position: relative;
        overflow: hidden;
    }

    #puzzle-board {
        width: 300px;
        height: 300px;
        position: absolute;
    }

    .puzzle-piece {
        width: 100px;
        height: 100px;
        position: absolute;
        background-size: 300px 300px;
        border: 2px solid #fff;
        transition: all 0.3s ease-in-out;
    }

    button {
        display: block;
        margin: 20px auto;
    }
</style>
  • #puzzle-container:这是拼图游戏容器的样式。它设置了容器的宽度和高度为300px,并使用margin: 0 auto使其水平居中对齐。还设置了边框样式和相对定位,并使用overflow: hidden属性隐藏超出容器大小的内容。

  • #puzzle-board:这是拼图游戏板的样式。它设置了游戏板的宽度和高度为300px,并使用position: absolute将其相对于父容器进行定位。

  • .puzzle-piece:这是拼图块的样式。它设置了拼图块的宽度和高度为100px,并使用position: absolute将其相对于父容器进行定位。background-size: 300px 300px用于调整拼图块背景图的大小以适应游戏板。还设置了边框样式和过渡效果,使拼图块在移动时具有平滑的过渡效果。

  • button:这是按钮的样式。display: block使按钮以块级元素显示,margin: 20px auto用于将按钮居中对齐。

以上是拼图游戏的基本CSS样式。通过这些样式,游戏容器、游戏板和拼图块将按照指定的样式进行布局和显示。你可以根据这个CSS代码继续完善拼图游戏的样式和外观。

在这里插入图片描述


✨ 逻辑部分

上面我们搭建了基本的样式,下面呢我们就通过js代码,实现我们游戏的功能吧,下面是对代码的简化解释:

好的,我将重点解释一些关键部分的代码。

  1. document.addEventListener('DOMContentLoaded', () => { ... }); 这段代码表示在文档加载完成后执行的函数,确保在操作DOM之前元素已经存在。

  2. const puzzleContainer = document.getElementById('puzzle-container'); 这行代码获取具有 id 为 “puzzle-container” 的元素,并将其存储在 puzzleContainer 变量中。这个容器元素是整个拼图游戏的外层容器。

  3. const puzzleBoard = document.getElementById('puzzle-board'); 这行代码获取具有 id 为 “puzzle-board” 的元素,并将其存储在 puzzleBoard 变量中。这个元素是用来放置拼图块的容器。

  4. const shuffleButton = document.getElementById('shuffle-btn'); 这行代码获取具有 id 为 “shuffle-btn” 的元素,并将其存储在 shuffleButton 变量中。这是一个按钮元素,用于触发打乱拼图的操作。

  5. const imageSrc = 'https://img1.baidu.com/it/u=1960110688,1786190632&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=281'; 这是一个存储图片地址的变量,用于设置拼图块的背景图片。当然这里是我在网上随机找的图片,你也可以换成你想要的图片。

  6. const rows = 3;const cols = 3; 这两行代码定义了拼图的行数和列数。

  7. const pieces = []; 这行代码创建了一个空数组,用于存储拼图块的DOM元素。

  8. let emptyPiece; 这是一个用于存储空拼图块的变量。

  9. let isShuffling = false; 这是一个标志位变量,用于判断是否正在打乱拼图。

  10. function createPuzzlePieces() { ... } 这是一个创建拼图块的函数。它根据行数和列数循环创建拼图块的DOM元素,并设置它们的样式、背景图片和位置。同时,为非空拼图块添加了点击事件监听器。

  11. function movePiece(piece) { ... } 这是一个移动拼图块的函数。它根据点击的拼图块和空拼图块的位置关系,交换它们的位置,并更新它们的样式和数据集。

  12. function checkWin() { ... } 这是一个检查是否完成拼图的函数。它遍历所有拼图块,根据其当前位置与正确位置进行比较,判断是否完成拼图。如果拼图完成,则显示恭喜的提示,并重新启用打乱按钮。

  13. function shufflePuzzle() { ... } 这是一个打乱拼图的函数。它使用随机算法将拼图块进行打乱,将空拼图和其他拼图块进行交换,并计数打乱次数。在每次交换拼图块后,检查是否完成拼图,如果拼图已经完成,则显示完成提示,并重新启用打乱按钮。

  14. createPuzzlePieces(); 这行代码调用 createPuzzlePieces() 函数,开始创建拼图块。

  15. shuffleButton.addEventListener('click', shufflePuzzle); 这行代码为打乱按钮添加了点击事件监听器,点击按钮将触发 shufflePuzzle() 函数,开始打乱拼图。

这些代码一起实现了一个基于DOM操作的拼图游戏。通过点击拼图块和打乱按钮,可以移动拼图块并打乱拼图,当拼图完成时会显示完成提示。

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>几何⚡️拼图</title>
    <style>
        #puzzle-container {
            width: 300px;
            height: 300px;
            margin: 0 auto;
            border: 2px solid #ccc;
            position: relative;
            overflow: hidden;
        }

        #puzzle-board {
            width: 300px;
            height: 300px;
            position: absolute;
        }

        .puzzle-piece {
            width: 100px;
            height: 100px;
            position: absolute;
            background-size: 300px 300px;
            border: 2px solid #fff;
            transition: all 0.3s ease-in-out;
        }

        button {
            display: block;
            margin: 20px auto;
        }
    </style>
</head>

<body>
    <h2>图片切换拼图</h2>
    <div id="puzzle-container">
        <div id="puzzle-board"></div>
    </div>
    <button id="shuffle-btn">打乱拼图</button>

</body>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        const puzzleContainer = document.getElementById('puzzle-container');
        const puzzleBoard = document.getElementById('puzzle-board');
        const shuffleButton = document.getElementById('shuffle-btn');
        const imageSrc = 'https://img1.baidu.com/it/u=1960110688,1786190632&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=281'; // 替换为你的图片地址
        const rows = 3;
        const cols = 3;
        const pieces = [];

        let emptyPiece;
        let isShuffling = false;

        // 创建拼图块
        function createPuzzlePieces() {
            const pieceWidth = puzzleContainer.offsetWidth / cols;
            const pieceHeight = puzzleContainer.offsetHeight / rows;
            for (let row = 0; row < rows; row++) {
                for (let col = 0; col < cols; col++) {
                    const piece = document.createElement('div');
                    piece.className = 'puzzle-piece';
                    piece.style.width = `${pieceWidth}px`;
                    piece.style.height = `${pieceHeight}px`;
                    piece.style.backgroundImage = `url(${imageSrc})`;
                    piece.style.backgroundPosition = `${-col * pieceWidth}px ${-row * pieceHeight}px`;
                    piece.style.top = `${row * pieceHeight}px`;
                    piece.style.left = `${col * pieceWidth}px`;
                    piece.dataset.row = row;
                    piece.dataset.col = col;

                    if (row === rows - 1 && col === cols - 1) {
                        emptyPiece = piece;
                        piece.classList.add('empty');
                    } else {
                        piece.addEventListener('click', () => {
                            if (!isShuffling) {
                                movePiece(piece);
                            }
                        });
                    }

                    puzzleBoard.appendChild(piece);
                    pieces.push(piece);
                }
            }
        }

        // 移动拼图块
        function movePiece(piece) {
            const emptyPieceRow = parseInt(emptyPiece.dataset.row);
            const emptyPieceCol = parseInt(emptyPiece.dataset.col);
            const pieceRow = parseInt(piece.dataset.row);
            const pieceCol = parseInt(piece.dataset.col);

            if (
                (pieceRow === emptyPieceRow && Math.abs(pieceCol - emptyPieceCol) === 1) ||
                (pieceCol === emptyPieceCol && Math.abs(pieceRow - emptyPieceRow) === 1)
            ) {
                const tempRow = emptyPieceRow;
                const tempCol = emptyPieceCol;
                emptyPiece.style.top = `${pieceRow * piece.offsetHeight}px`;
                emptyPiece.style.left = `${pieceCol * piece.offsetWidth}px`;
                emptyPiece.dataset.row = pieceRow;
                emptyPiece.dataset.col = pieceCol;

                piece.style.top = `${tempRow * piece.offsetHeight}px`;
                piece.style.left = `${tempCol * piece.offsetWidth}px`;
                piece.dataset.row = tempRow;
                piece.dataset.col = tempCol;
            }

            checkWin();
        }

        let isWin = false; // 添加标志位

        // 检查是否完成拼图
        function checkWin() {
            let isPuzzleComplete = true;
            for (let i = 0; i < pieces.length; i++) {
                const piece = pieces[i];
                const row = parseInt(piece.dataset.row);
                const col = parseInt(piece.dataset.col);
                if (row !== Math.floor(i / cols) || col !== i % cols) {
                    isPuzzleComplete = false;
                    break;
                }
            }

            if (isPuzzleComplete && !isWin && !isShuffling) { // 添加条件判断
                isWin = true; // 设置标志位为true
                setTimeout(() => {
                    alert('恭喜!你完成了拼图!');
                    shuffleButton.disabled = false;
                    isWin = false; // 重置标志位为false
                }, 200);
            }
        }
        // 打乱拼图
        function shufflePuzzle() {
            isShuffling = true;
            shuffleButton.disabled = true;
            isWin = false; // 重置标志位为false

            const shuffleCount = 100;
            let count = 0;

            const intervalId = setInterval(() => {
                const randomIndex = Math.floor(Math.random() * pieces.length);
                const randomPiece = pieces[randomIndex];
                movePiece(randomPiece);
                count++;

                if (count >= shuffleCount) {
                    clearInterval(intervalId);
                    isShuffling = false;
                    shuffleButton.disabled = false;
                }
            }, 10);
        }

        createPuzzlePieces();
        shuffleButton.addEventListener('click', shufflePuzzle);
    });

</script>

</html>

本期推荐 查看详情

参与方式:本博客中进行评论即可,只要评论内容不被折叠都可以参与抽奖;

抽奖方式:程序自动拉取未折叠的评论随机抽取5位伙伴,每人最多可评论三次;

抽奖时间:2023-06-09 17:00

本期获奖者各送实体书《Spring Cloud 微服务快速上手》一本(包邮到家)

在这里插入图片描述

原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/622603.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python数组:如何使用Python将数据存入数组中

Python数组&#xff1a;如何使用Python将数据存入数组中 Python是一个非常强大的高级编程语言&#xff0c;在人工智能、数据科学和机器学习等领域有广泛应用。Python允许将数据存储在不同的数据结构中&#xff0c;其中&#xff0c;数组是最常用的。 在这篇文章中&#xff0c;…

卡尔曼滤波与组合导航原理(一)滤波的基本概念、递推最小二乘

文章目录 一、滤波的基本概念1、传统数字滤波器2、现代控制中的状态观测器3、最优估计的含义4、温度估计的例子1.问题描述2.分析 二、递推最小二乘 课程链接&#xff1a;https://www.bilibili.com/video/BV11K411J7gp/?p1 参考书目&#xff1a;《捷联惯导算法与组合导航原理》…

微信小程序nodejs+vue+uniapp大学体育场馆场地预约系统

开发语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 本文以nodejs为开发技术&#xff0c;一开始&#xff0c;本文就对系统内谈到的基本知识&#xff0c;从整体上进行了描述&#xff0c…

人事管理项目-部门数据删除

人事管理项目-部门数据删除 后端实现前端实现 Spring Boot是一个框架&#xff0c;一种全新的编程规范&#xff0c;它的产生简化了框架的使用&#xff0c;所谓简化是指简化了Spring众多框架中所需的大量且烦琐的配置文件&#xff0c;所以Spring Boot是一个服务于框架的框架&…

Netty之Channel解读

目录 channel 的主要作用 ChannelFuture CloseFuture 为什么要异步关闭 channel 的主要作用 close() 可以用来关闭 channelcloseFuture() 用来处理 channel 的关闭 sync 方法作用是同步等待 channel 关闭而 addListener 方法是异步等待 channel 关闭pipeline() 方法添加处理…

使用CodeAnt查找并修复IDE中的开源漏洞与许可证合规问题

不断加快的开发步伐正在将软件安全的责任转移到开发人员的桌面上&#xff0c;但是处理在下游构建和测试中检测到的安全问题可能是非常具有破坏性的。直至报告漏洞的时候&#xff0c;开发人员已经转移到他们的下一个任务。为了修复问题&#xff0c;他们必须中断正在做的事情&…

【OpenAI】Python:基于 Gym-CarRacing 的自动驾驶项目(4) | 车辆控制功能的实现

猛戳&#xff01;跟哥们一起玩蛇啊 &#x1f449; 《一起玩蛇》&#x1f40d; ​ &#x1f4ad; 写在前面&#xff1a;本篇是关于多伦多大学自动驾驶专业项目的博客。GYM-Box2D CarRacing 是一种在 OpenAI Gym 平台上开发和比较强化学习算法的模拟环境。它是流行的 Box2D 物…

Faiss PQ 乘积量化

Approximate Nearest Neighbor搜索简称ANN。 从宏观上看ANN brute-force搜索的方式是在全空间进行搜索&#xff0c;为了加快查找的速度&#xff0c;几乎所有的ANN方法都是通过对全空间分割&#xff0c;将其分割成很多小的子空间&#xff0c;在搜索的时候&#xff0c;通过某种…

爬虫分布式爬虫部署知识详解

分布式爬虫是指将一个爬虫任务分解成多个子任务&#xff0c;由多个爬虫节点同时执行&#xff0c;以提高爬取效率和速度的一种爬虫方式。下面是分布式爬虫部署的详细步骤&#xff1a; 确定爬虫任务&#xff1a;首先需要确定要爬取的网站和数据&#xff0c;以及需要爬取的频率和深…

公牛33W车充评测 | 拓尔微 IM2403+TMI3451快充方案实力在线

本期嘉宾是我们的老熟人 公牛PD 33W的车载充电器&#xff0c;此前我们对其进行过拆解&#xff0c;那它的充电表现和各方面性能到底如何呢&#xff1f; 下面我们将围绕协议测试、供电方案和产品测试展开评测解读&#xff0c;帮助小伙伴们全方位了解这款1A1C双口快充车充及相应的…

maven私服搭建详细教程(看完必会)

目录 1 为什么需要私服 2 Nexus私服 2.1 Nexus下载及登录 2.2 maven仓库 2.2.1 代理仓库 2.2.2 宿主仓库 2.2.3 仓库组 3 本地Maven下载构建 3.1 pom.xml方式 3.2 镜像方式 4 本地依赖发布到私服 4.1 maven部署到nexus私服 4.1.1 快照版本 ​4.1.2 release版本 4.2 …

Jar包下载失败的解决方案

Jar包下载失败的解决方案 &#x1f50e;配置阿里源&#x1f50e;重新下载Jar包&#x1f50e;结尾 &#x1f50e;配置阿里源 点击 Settings 搜索 Maven 进行如下修改 注意&#x1f36d; User settings file 路径与 Local repository 路径中应尽量避免出现中文 搜索 User setti…

如何提高高层住宅的消防安全性?安科瑞 许敏

1高层住宅消防安全隐患特点 根据我国对高层住宅的规定&#xff0c;建筑高度大于54m的住宅建筑&#xff08;包括设置商业服务网点的住宅建筑&#xff09;为一类高层住宅建筑&#xff0c;建筑高度大于27m&#xff0c;但不大于54m的住宅建筑&#xff08;包括设置商业服务网点的住宅…

JAVA代码程序如何调用电商API,获取电商数据?

电商API是为了实现各个电商平台之间数据交换而提供的网络接口。Java是一种流行的编程语言&#xff0c;可以通过调用API来获得电商平台提供的各种服务&#xff0c;如商品列表、订单状态等。在这篇文章中&#xff0c;我们将详细介绍如何使用Java代码调用电商API。 1.寻找电商平台…

Python:Proportional Odds Model (POM)序分类比例几率模型

Github上你找不到 Logistic 函数求导看这里

责任链实战场景剖析、以及手写责任链

前言&#xff1a; 最早接触责任链这个设计模式&#xff0c;是我老早前看 Spring Aop 的源码的时候&#xff0c;Aop 的原理是遍历一根按照顺序装载好的 Advice&#xff08;通知&#xff09;拦截器链条&#xff0c;使Before、After 这些 Advice&#xff08;通知&#xff09;中的逻…

Accountill 使用 MongoDB、Express、React 和 Nodejs (MERN) 制作的全栈开源发票应用程序

Accountill 使用 MongoDB、Express、React 和 Nodejs (MERN) 制作的全栈开源发票应用程序。 介绍 使用 MERN 堆栈&#xff08;MongoDB、Express、React 和 Nodejs&#xff09;制作的全栈发票应用程序&#xff0c;专为自由职业者和小型企业设计&#xff0c;几乎可用于任何类型的…

职场中有哪些不成熟的表现

(点击即可收听) 大家好,这里是人人领读,今天给大家分享的,职场中有哪些不成熟的表现,希望能给大家带来一些启发. 1. 不主动汇报自己的工作进度 这个在职场当中,是非常忌讳的,一定要积极反馈,不能闷声憋着,说什么自己社恐,不敢跟上级领导交流,害怕被说 自己被分配的任务做到哪个…

仙人掌之歌——权力的游戏(4)

技术大培训 周一上午&#xff0c;陈速在工位上有些坐立不安&#xff0c;他也不知道自己在等待着什么。脑子里不可遏止地又想起上周五时&#xff0c;易伟成过来找自己说的那些没头没脑的话。易伟成先是询问直播串的参数细节&#xff0c;因为他要设计播放串加密方案&#xff0c;…

大数据架构系列:如何理解湖仓一体?

转载&#xff1a;如有侵权&#xff0c;告知即删除 引言 这十多年大数据技术蓬勃发展&#xff0c;从市场的表现来看基于大数据的数据存储和计算是非常有价值的&#xff0c;其中以云数据仓库为主打业务的公司Snowflake市值最高&#xff08;截止当前449亿美元&#xff09;&#x…