🏠中秋佳节,万家团圆:中秋拼图小游戏。

806 阅读6分钟

前言:提前预祝各位开发者、各行各业的工作人员,中秋佳节!国庆节~身体健康,阖家欢乐!!!

前言:马上就到了7+3=8的日子了~中秋节和国庆碰撞在一起。“日升月落,星烁云遮,溪河入海,红旗飘扬,神州大地之上,东方大国重立,皆是顺天而行理当繁荣昌盛。” 在这个日子里,为何不来玩玩有趣又土到掉牙的拼图游戏呢!!!?

在这个拼图游戏中,我们会展示一张月饼图片,然后将它分割成多个小方块。我们需要拖拽这些小方块,使它们重新排列,最终呈现出完整的图片。

话不多说,先看效果!

框架我们采用的是React,因为也是第一次写拼图小游戏,某些代码有进行Google。其中图片是随便找的一张哈~

简单介绍下,首先,我们需要引入ReactuseStateuseCallback这三个模块,并使用ReactDOM进行 DOM 渲染。然后,定义一个二维数组data,存储每个小方块的背景位置信息。声明一个函数组件Test,在组件内部使用useState来创建两个状态变量。backgroundPositions用于保存小方块的背景位置信息,默认值为data数组。

后面我们定义了一些函数包括handleOneKeyCompletehandleStartGamehandleSeeOriginalImagehandleDragStarthandleDrophandleDragOver。这些函数分别处理重新排列方块、打乱方块顺序以及拖拽方块等操作。

在组件的返回结果中,使用map方法遍历backgroundPositions数组,为每个小方块创建一个div元素。设置div元素的背景位置样式为对应的position,并添加拖拽事件处理函数。最后,我们将组件渲染到idapp的 DOM 节点上。

下面我们就开始一一介绍学习如何做出这样的游戏~~~

代码解析:布局

首先映入眼帘的是这个结构。

<div>
     <div className="box">
        {backgroundPositions.map((position, index) => (
          <div
            key={index}
            className="d1"
            style={{ backgroundPosition: position }}
            draggable
            onDragStart={(e) => handleDragStart(e, index, position)}
            onDrop={(e) => handleDrop(e, index, position)}
            onDragOver={handleDragOver}
          ></div>
        ))}
      </div>
      <div id="but">
        <button onClick={handleStartGame}>开始游戏</button>
        <div style={{width:'30%'}}>
          <img id="yan" src="https://img.zcool.cn/community/0165245d71ad6fa801202f17f3702d.jpg@1280w_1l_2o_100sh.jpg" alt="" />
        </div>
      </div>
    </div>

它由两个主要部分组成:一个包含拼图方块的容器和一个按钮加图片的容器。

拼图方块的容器

使用了一个div元素,并给它添加了一个box的类名。在这个容器内部,使用map方法遍历backgroundPositions数组,为每个小方块创建一个div元素。这些小方块的类名为d1,并且设置了背景位置样式为对应的position,通过style属性进行设置。同时,为了实现拖拽功能,给每个小方块添加了draggable属性,并绑定了相应的拖拽事件处理函数handleDragStarthandleDrophandleDragOver。这样,用户可以通过拖拽这些小方块来重新排列拼图。

按钮和图片的容器。

使用了一个div元素,它的id属性为but。在这个容器内部,包含一个按钮和一张图片。按钮使用了button元素,绑定了点击事件处理函数handleStartGame,用于开始游戏。

代码解析:CSS

其实这段css不用怎么介绍,很简单,直接看代码就可以。如果需要替换图片,可以直接修改d1里面的background-image

        * {
            margin: 0;
            padding: 0;
        }
 
        .box {
            width: 312px;
            height: 312px;
            border: 3px solid #000;
            margin: 50px auto 5px;
            font-size: 0;
        }
 
        .box div {
            width: 100px;
            height: 100px;
            display: inline-block;
            border: 2px solid #000;
        }
 
        .d1 {
            background-image: url('https://img.zcool.cn/community/0165245d71ad6fa801202f17f3702d.jpg@1280w_1l_2o_100sh.jpg');
            background-size: 300px 300px;
            background-position: 0px 0px;
        }
 这里我稍作解释下,每个拼图方块小块。设置背景图片为指定的URL,URL指向一张拼图的原始图片,设置背景尺寸为300像素乘以300像素,初始背景位置为0像素左偏移和0像素上偏移。
 
        #but {
            border: 1px solid #000 transparent;
            width: 300px;
            height: 30px;
            margin: 0 auto;
        }
 
        #but img {
            width: 100px;
            height: 100px;
            float: right;
            display: none;
        }
 
        button {
            margin: 1px auto;
            border: 1px solid #000;
        }

代码解析:方法

我个人认为,如果不熟悉的新手,难点可能就在拖动上面。

当然,以下是对代码的详细解释和相关代码片段:

const data = [['0 0'], ['-100px 0'], ['-200px 0'], ['0 -100px'], ['-100px -100px'], ['-200px -100px'], ['0 -200px'], ['-100px -200px'], ['-200px -200px']];

这是包含了拼图方块初始位置信息的二维数组。每个元素代表一个方块的位置,格式为[x y],其中xy表示方块在CSS中的背景定位。

const [backgroundPositions, setBackgroundPositions] = useState(data);

这是使用React的useState钩子创建了一个名为backgroundPositions的状态变量,其初始值为上述的data数组。setBackgroundPositions是用于更新backgroundPositions状态的函数。

const handleStartGame = () => {
  const arr = [];
  let maxTimes = 9;

  do {
    const num = Math.floor(Math.random() * 9);
    if (arr.indexOf(num) === -1) {
      arr.push(num);
      maxTimes--;
    }
  } while (maxTimes);

  const newBackgroundPositions = arr.map((value, index) => backgroundPositions[value]);
  setBackgroundPositions(newBackgroundPositions);
};

handleStartGame是开始游戏的函数。它首先创建一个空数组arr和一个最大次数maxTimes,并使用do-while循环生成一个随机数num,直到maxTimes减为0为止。如果arr数组中没有这个随机数,就将其添加到arr数组中,并将maxTimes减1。然后,使用map方法根据打乱的顺序获取新的背景位置数组newBackgroundPositions。最后,通过调用setBackgroundPositions函数将新的背景位置数组设置为更新后的背景位置。

const handleDragStart = (e, id, position) => {
  e.dataTransfer.setData('divID', id);
  e.dataTransfer.setData('divPosition', position);
};

handleDragStart 这个方法就是用来拖动图片的啦。当拼图方块被拖动时,将该方块的idposition存储在dataTransfer对象中,用于在拖放过程中传递信息。

const handleDrop = (e, id, position) => {
  e.preventDefault();
  e.stopPropagation();

  const droppedDivId = e.dataTransfer.getData('divID');
  const droppedDivPosition = e.dataTransfer.getData('divPosition');

  const newBackgroundPositions = [...backgroundPositions];
  const originalBackgroundPosition = newBackgroundPositions[id];
  newBackgroundPositions[id] = droppedDivPosition;
  newBackgroundPositions[droppedDivId] = originalBackgroundPosition;

  setBackgroundPositions(newBackgroundPositions);
};

handleDrop这个方法和上面的是联系起来,是用于放置图片,如果拼图方块被释放时,会执行此函数。首先,阻止默认的拖放行为(例如,打开拖放文件时的默认行为)。接着,阻止事件冒泡,以确保不会触发外层元素上的拖放处理程序。

然后,通过dataTransfer对象获取之前存储的拖拽方块的idposition。创建一个新的背景位置数组newBackgroundPositions,将原本在目标位置上的背景位置替换为拖动方块的position,同时将原本在拖动方块位置上的背景位置替换为目标位置的position。最后,通过调用setBackgroundPositions函数将新的背景位置数组设置为更新后的背景位置。

const handleDragOver = (e) => {
  e.preventDefault();
};

这可以处理拖放过程中的拖动目标元素上的事件的函数handleDragOver。它可以阻止了默认的拖放行为!

最后我们来试试拼一张图片吧~

试试看吧,你也可以

本文同步我的技术文档