微信小程序实现的数字滑块拼图效果

 更新时间:2024年08月09日 12:21:20   作者:yoho-zhang  
滑块拼图(Slider Puzzle)是一种经典的智力游戏,通常由一个3x3或更大的格子组成,其中一个格子为空,玩家通过滑动拼图块来达到特定的图案或顺序,这篇文章主要介绍了微信小程序实现的数字滑块拼图,需要的朋友可以参考下

微信小程序实现的数字滑块拼图

【注】本文章只是记录下实现的过程,个人喜好,无技术指导倾向,感兴趣的小伙伴可以继续往下看下实现过程,

总体实现效果如下图,第一个界面是拼图初始界面,第二个是拼图过程界面

游戏玩法介绍:

滑块拼图(Slider Puzzle)是一种经典的智力游戏,通常由一个3x3或更大的格子组成,其中一个格子为空,玩家通过滑动拼图块来达到特定的图案或顺序。

按钮介绍

“开始/暂停” 按钮

初次进入页面,需要点击开始按钮,才能进行游戏,第一次点击后,屏幕上面的计时器会开始计时,再次点击暂停按钮,计时停止,游戏界面锁定

“重置”按钮

将正在进行中的游戏进行还原初始化,重新开始

代码实现

代码整体目录结构如下:

├── app.js
├── app.json
├── app.wxss
├── pages
│   ├── index
│   │   ├── index.js
│   │   ├── index.json
│   │   ├── index.wxml
│   │   └── index.wxss
├── project.config.json
├── project.private.config.json
├── sitemap.json
└── utils
    └── util.js

感兴趣的小伙伴可以下载看下
源码下载:https://gitee.com/ZYHHYZ/slider_puzzles

app.json

该文件仅仅是定义了一个导航条名称为 滑块拼图

{
  "pages": [
    "pages/index/index"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "滑块拼图",
    "navigationBarTextStyle": "black"
  }
}

page

page文件夹 只包含 一个index页面

index页面

index.wxml

代码如下

<view class="container">
  <!-- 计时器  -->
  <view class="timer-row">
        <view class="timer-text timer-text-m">{{ timerM }}</view>
        <view class="timer-sp timer-text-sp">:</view>
        <view class="timer-text timer-text-s">{{ timerS }}</view>
        <view class="timer-sp timer-text-sp">:</view>
        <view class="timer-text timer-text-ms">{{ timerMs }}</view>
    </view>
   <!-- 滑块  -->
   <view class="puzzle-container">
     <block wx:for="{{tiles}}" wx:key="*this">
       <view class="puzzle-tile" bind:tap="onclickEvent" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd" data-index="{{index}}">
         {{item}}
       </view>
     </block>
   </view>
   <!-- 按钮区  -->
   <view class="button-container">
          <button class="start-button" bindtap="toggleTimer">开始/停止</button>
          <button class="restart-button" bindtap="restartPuzzle">重置</button>
     </view>
</view>

index.js

Page({
  data: {
    successTiles: [1, 2, 3, 4, 5, 6, 7, 8, ''],
    tiles: [1, 2, 3, 4, 5, 6, 7, 8, ''],
    emptyIndex: 8,
    selector: {
      x: 0,
      y: 0,
      index: 0
    },
    isGameing: false,
    timer: '00:00:00',
    timerM: '00',
    timerS: '00',
    timerMs: '00',
    timerRunning: false,
    timerInterval: null
  },
  onLoad: function () {
  },
  touchStart: function (e) {
    // console.log("移动开始", e.currentTarget.dataset)
    if(!this.data.timerRunning){
      console.log("请点击 开始按钮")
      return
    }
    const touch = e.touches[0];
    let index = e.currentTarget.dataset.index;
    let xy = this.getXY(index, 3)
    // console.log(xy)
    this.setData({
      startX: touch.clientX,
      startY: touch.clientY,
      selector: xy
    });
  },
  touchMove: function (e) {
    // console.log("移动进行中", e)
    const touch = e.touches[0];
    this.setData({
      moveX: touch.clientX,
      moveY: touch.clientY,
    });
  },
  touchEnd: function (e) {
    // console.log("移动结束", e)
    const endX = this.data.moveX;
    const endY = this.data.moveY;
    const startX = this.data.startX;
    const startY = this.data.startY;
    const deltaX = endX - startX;
    const deltaY = endY - startY;
    // console.log("deltaX ", deltaX," deltaY ", deltaY)
    if (Math.abs(deltaX) > Math.abs(deltaY)) {
      // 水平滑动
      if (deltaX > 30) {
        // 向右滑动
        // console.log("→")
        this.slideTiles('right');
      } else if (deltaX < -30) {
        // 向左滑动
        // console.log("←")
        this.slideTiles('left');
      }
    } else {
      // 垂直滑动
      if (deltaY > 30) {
        // 向下滑动
        // console.log("↓")
        this.slideTiles('down');
      } else if (deltaY < -30) {
        // 向上滑动
        // console.log("↑")
        this.slideTiles('up');
      }
    }
  },
  slideTiles: function (direction) {
    // 根据方向交换空白块和相邻块
    // 这里需要添加实际的交换逻辑,确保空白块可以移动
    // 例如,你可以在这里交换两个块的位置,但要确保空白块可以移动
    // 以下是一个简单的示例,实际逻辑会更复杂
    let newTiles = this.data.tiles.slice();
    let emptyIndex = this.data.emptyIndex;
    let selector = this.data.selector;
    emptyIndex = selector.index
    console.log(selector, direction)
    switch (direction) {
      case 'right':
        if (emptyIndex % 3 < 2) {
          // 判断相邻的要移动的是否为空白块
          if (newTiles[emptyIndex + 1] != '') {
            console.log("不能移动", direction)
            return
          }
          [newTiles[emptyIndex], newTiles[emptyIndex + 1]] = [newTiles[emptyIndex + 1], newTiles[emptyIndex]];
          this.setData({
            tiles: newTiles,
            emptyIndex: emptyIndex + 1
          });
        }
        break;
      case 'left':
        if (emptyIndex % 3 > 0) {
          if (newTiles[emptyIndex - 1] != '') {
            console.log("不能移动", direction)
            return
          }
          [newTiles[emptyIndex], newTiles[emptyIndex - 1]] = [newTiles[emptyIndex - 1], newTiles[emptyIndex]];
          this.setData({
            tiles: newTiles,
            emptyIndex: emptyIndex - 1
          });
        }
        break;
      case 'up':
        if (emptyIndex >= 3) {
          if (newTiles[emptyIndex - 3] != '') {
            console.log("不能移动", direction)
            return
          }
          [newTiles[emptyIndex], newTiles[emptyIndex - 3]] = [newTiles[emptyIndex - 3], newTiles[emptyIndex]];
          this.setData({
            tiles: newTiles,
            emptyIndex: emptyIndex - 3
          });
        }
        break;
      case 'down':
        if (emptyIndex < 6) {
          if (newTiles[emptyIndex + 3] != '') {
            console.log("不能移动", direction)
            return
          }
          [newTiles[emptyIndex], newTiles[emptyIndex + 3]] = [newTiles[emptyIndex + 3], newTiles[emptyIndex]];
          this.setData({
            tiles: newTiles,
            emptyIndex: emptyIndex + 3
          });
        }
        break;
    }
    this.isSuccess()
  },
  isSuccess: function () {
    let ti = this.data.tiles
    let su = this.data.successTiles
    console.log(su == ti)
    let ti_str = ti.join(',')
    let su_str = su.join(',')
    // console.log(ti_str == su_str)
    // console.log("isSuccess", ti, ti_str, su_str)
    if (ti_str == su_str) {
      this.stopTimer();
      wx.showToast({
        title: '操作成功',
        icon: 'success',
        duration: 2000
      });
    }
  },
  shuffleArray: function (array) {
    for (let i = array.length - 1; i > 0; i--) {
      // 生成一个随机索引从 0 到 i
      const j = Math.floor(Math.random() * (i + 1));
      // 交换元素
      [array[i], array[j]] = [array[j], array[i]];
    }
    return array;
  },
  onclickEvent: function (e) {
    if(!this.data.isGameing){
      console.log("请点击 开始按钮")
      return
    }
    let index = e.currentTarget.dataset.index;
    let newTiles = this.data.tiles.slice();
    let tiles = this.data.tiles
    let currentTile = tiles[index]
    if (currentTile == "") {
      // console.log("不能移动")
      return
    }
    let upIndex, downIndex, leftIndex, rightIndex = null;
    // 查询 上下左右的索引
    let xy = this.getXY(index, 3)
    let selector = this.data.selector;
    let emptyIndex = selector.index
    // 可以向右移动
    if (emptyIndex % 3 < 2) {
      if (newTiles[emptyIndex + 1] == '') {
        [newTiles[emptyIndex], newTiles[emptyIndex + 1]] = [newTiles[emptyIndex + 1], newTiles[emptyIndex]];
        this.setData({
          tiles: newTiles,
          emptyIndex: emptyIndex + 1
        });
      }
    }
    // 可以向左运动
    if (emptyIndex % 3 > 0) {
      if (newTiles[emptyIndex - 1] == '') {
        [newTiles[emptyIndex], newTiles[emptyIndex - 1]] = [newTiles[emptyIndex - 1], newTiles[emptyIndex]];
        this.setData({
          tiles: newTiles,
          emptyIndex: emptyIndex - 1
        });
      }
    }
    // 可以向上运动
    if (emptyIndex >= 3) {
      if (newTiles[emptyIndex - 3] == '') {
        [newTiles[emptyIndex], newTiles[emptyIndex - 3]] = [newTiles[emptyIndex - 3], newTiles[emptyIndex]];
        this.setData({
          tiles: newTiles,
          emptyIndex: emptyIndex - 3
        });
      }
    }
    // 向下运动
    if (emptyIndex < 6) {
      if (newTiles[emptyIndex + 3] == '') {
        [newTiles[emptyIndex], newTiles[emptyIndex + 3]] = [newTiles[emptyIndex + 3], newTiles[emptyIndex]];
        this.setData({
          tiles: newTiles,
          emptyIndex: emptyIndex + 3
        });
      }
    }
    console.log("点击事件", xy, )
  },
  getXY: function (index, num) {
    let x = Math.floor(index / num) + 1
    let y = index % num + 1
    return {
      x,
      y,
      index
    }
  },
  startTimer: function () {
    if (!this.data.timerRunning) {
      let tiles = this.data.tiles
      let isGameing = this.data.isGameing
      if (!this.data.isGameing) {
        // 游戏未开始 ,打乱顺序
        tiles = this.shuffleArray(tiles)
        isGameing = true
      }
      this.setData({
        tiles: tiles,
        timerRunning: true,
        isGameing: isGameing
      });
      this.data.timerInterval = setInterval(() => {
        let timers = this.data.timer.split(":")
        let m = Number(timers[0])
        let s = Number(timers[1])
        let ms = Number(timers[2])
        if (ms < 99) {
          ms += 1
        } else {
          ms = 0
          if (s < 59) {
            s = s + 1
          } else {
            s = 0
            m += 1
          }
        }
        let timestr = `${this.formatNumber(m,2)}:${this.formatNumber(s,2)}:${this.formatNumber(ms,2)}`
        console.log("--------", timers, timestr)
        this.setData({
          timer: timestr,
          timerM:this.formatNumber(m,2),
          timerS:this.formatNumber(s,2),
          timerMs:this.formatNumber(ms,2)
        });
      }, 10);
    }
  },
  stopTimer: function () {
    if (this.data.timerRunning) {
      this.setData({
        timerRunning: false
      });
      clearInterval(this.data.timerInterval);
    }
  },
  toggleTimer: function () {
    if (this.data.timerRunning) {
      this.stopTimer();
    } else {
      this.startTimer();
    }
  },
  restartPuzzle:function(){
    // let tiles = this.data.tiles
    // tiles = this.shuffleArray(tiles)
    this.stopTimer()
    this.setData({
      successTiles: [1, 2, 3, 4, 5, 6, 7, 8, ''],
      tiles: [1, 2, 3, 4, 5, 6, 7, 8, ''],
      emptyIndex: 8,
      selector: {
        x: 0,
        y: 0,
        index: 0
      },
      isGameing: false,
      timer: '00:00:00',
      timerM: '00',
      timerS: '00',
      timerMs: '00',
      timerRunning: false,
      timerInterval: null
    })
// this.startTimer()
  },
  formatNumber: function (num, length) {
    return num.toString().padStart(length, '0');
  },
});

index.wxss

.container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #fffaf0;
  font-family: 'Comic Sans MS', cursive, sans-serif;
}
.timer-row {
  display: flex;
  align-items: center;
  margin-bottom: 100px;
  animation: blink 2s infinite;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  border: 1px solid #ffb6c1;
  border-radius: 15px;
  padding: 15px;
}
.timer-text {
  width: 90px;
  height: 100px;
  background-color: #ffcccc;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 64px; /* 增大字体大小 */
  font-weight: bold; /* 加粗字体 */
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: transform 0.3s ease;
  /* border-radius: 5px; */
  color: #ffffff; /* 粉色调字体 */
}
.timer-sp {
  width: 10px;
  height: 100px;
  background-color: #ffcccc;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 64px; /* 增大字体大小 */
  font-weight: bold; /* 加粗字体 */
  position: relative;
  color: #ffffff; /* 粉色调字体 */
}
@keyframes blink {
  0% { opacity: 1; }
  50% { opacity: 0.5; }
  100% { opacity: 1; }
}
/*  */
.button-container {
  display: flex;
  justify-content: space-between; /* 使按钮分布在容器的两端 */
  width: 100%; /* 容器宽度为100% */
  margin-top: 50px; /* 与拼图块之间添加一些间距 */
}
.start-button {
  background-color: hsl(0, 100%, 90%);
  color: #cf77a3;
  width: 110px;
  border: 1px solid #ffb6c1;
  padding: 5px 10px;
  border-radius: 5px;
  font-size: 18px;
  font-weight: bolder;
  cursor: pointer;
}
.restart-button {
  background-color: #ffcccc;
  color: #cf77a3;
  border: 1px solid #ffb6c1;
  padding: 5px 10px;
  width: 110px;
  border-radius: 5px;
  font-size: 18px;
  font-weight: bold;
  cursor: pointer;
}
/* 滑块样式 */
.puzzle-container {
  margin-top: -50px;
  display: grid;
  grid-template-columns: repeat(3, 100px);
  grid-gap: 10px;
  perspective: 1000px;
}
.puzzle-tile {
  width: 100px;
  height: 100px;
  background-color: #ffcccc;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 32px;
  font-weight: bold;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  border: 1px solid #ffb6c1;
  transition: transform 0.3s ease;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  border-radius: 5px;
  color: #ff69b4;
}
.puzzle-tile {
  width: 100px;
  height: 100px;
  background-color: #ffcccc;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 64px; /* 增大字体大小 */
  font-weight: bold; /* 加粗字体 */
  cursor: pointer;
  position: relative;
  overflow: hidden;
  border: 1px solid #ffb6c1;
  transition: transform 0.3s ease;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  border-radius: 5px;
  color: #ffffff; /* 粉色调字体 */
}
/* 当拼图块被点击时,添加一些动画效果 */
.puzzle-tile:active {
  transform: scale(0.5);
}

到此这篇关于微信小程序实现的数字滑块拼图的文章就介绍到这了,更多相关微信小程序数字滑块内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论