Vue拼图小游戏

本文详细介绍使用Vue.js创建互动拼图游戏的过程,包括主组件App.vue与子组件Puzzle.vue的设计,通过实例演示如何实现拼图逻辑,如图片加载、块状元素布局、点击交换与胜利检测等功能。

创建两个文件,一个是App.vue,另一个是Puzzle.vue,App.vue是主文件,Puzzle.vue是子组件

<template>
    <div>
        <d-puzzle v-bind="puzzleConfig[leval]" @next="handleNext"/>
        <button @click="handleNext">next</button>
    </div>
</template>

<script>
import DPuzzle from './Puzzle';

export default {
    components:{
        DPuzzle
    },
    data(){
        return{
            leval: 0,
            puzzleConfig: [
                {row: 3, col: 3, img: './puzzleImg/a1.jpg'},
                {row: 4, col: 4, img: './puzzleImg/a2.jpg'},
                {row: 5, col: 5, img: './puzzleImg/a3.jpg'},
                {row: 8, col: 8, img: './puzzleImg/a4.jpg'}
            ]
        }
    },
    methods: {
        handleNext(){
            console.log("aaa");
            this.leval++;
            if(this.leval == this.puzzleConfig.length){
                const answerFlag = window.confirm("最后一关,再开始不?");
                if(answerFlag){
                    this.leval = 0;
                }
            }
            console.log("next");
        }
    }
}
</script>

<style>
</style>

后面是Puzzle.vue

<template>
  <div class="puzzle" :style="{width: width+'px', height: height+'px'}">
    <!-- 前面的key需要获取一个唯一值,但是后面的blockPotints里面是对象,但是唯一值不能为对象,所以这里定义了一个唯一的id
         backgroundImage这里注意需要使用反向单引号,实现代码
         backgroundPostion定义图片的位置
         最后设置一个opacity将图片中的最后一个设置为透明的
         绑定ref将前面的图片设置为block,最后一张图片设置为empty
         绑定自定义属性data-correctX和data-correctY,供下面使用-->
    <div
      class="puzzle__block"
      v-for="(item, index) in blockPoints"
      :key="item.id"
      :style="{
        width: blockWidth+'px', 
        height: blockHeight+'px',
        left: item.x+'px',
        top: item.y+'px',
        backgroundImage: `url(${img})`,
        backgroundPosition: `-${correctPoints[index].x}px -${correctPoints[index].y}px`,
        opacity: index === blockPoints.length - 1 && 0 
        }"
      @click="handleClick"
      :ref="index === blockPoints.length - 1 ? 'empty': 'block'"
      :data-correctX="correctPoints[index].x"
      :data-correctY="correctPoints[index].y"
    />
  </div>
</template>

<script>
export default {
  props: {
    width: {
      type: Number,
      default: 500
    },
    height: {
      type: Number,
      default: 500
    },
    row: {
      type: Number,
      default: 3
    },
    col: {
      type: Number,
      default: 3
    },
    img: {
      type: String,
      required: true
    }
  },
  methods: {
    /**
     * handleClick方法绑定在各个小图片上
     * 该方法主要实现的就是图片的交换,也就是点击的图片和空白图片的交换
     * 使用e获取事件,e.target获取事件对象,也就是被点击的那一个
     * 使用left,top获取被点击对象的left和top
     * 使用emptyDom获取前面绑定的ref中的空白图片,也就是empty[0]
     * 交换emptyDom和blockDom的top和left
     */
    handleClick(e) {
      const blockDom = e.target;
      const emptyDom = this.$refs.empty[0];
      const { left, top } = blockDom.style;
      //   if (!this.isAdjacent(blockDom, emptyDom)) {
      //     return;
      //   }
      blockDom.style.left = emptyDom.style.left;
      blockDom.style.top = emptyDom.style.top;
      emptyDom.style.left = left;
      emptyDom.style.top = top;
      const winFlag = this.chackWin();
      if (winFlag) {
        this.winGame(emptyDom);
        console.log("success");
      }
    },
    /**
     * 实现的主要功能是判断是否是邻近的图片,也就是逻辑中只能是相邻的图片进行交换
     * 获取block的left,top,width,height,left和top改了名字domleft,domtop
     * 获取empty的left,top,前面已经获取了width和height这里就不必再获取了
     * 判断domtop - emptytop的绝对值是不是等于width, 判断domleft - emptyleft的绝对值是不是等于height
     */
    isAdjacent(blockDom, emptyDom) {
      const { left: domleft, top: domtop, width, height } = blockDom.style;
      const { left: emptyleft, top: emptytop } = emptyDom.style;
      const xDis = Math.floor(
        Math.abs(parseFloat(domleft) - parseFloat(emptyleft))
      );
      const yDis = Math.floor(
        Math.abs(parseFloat(domtop) - parseFloat(emptytop))
      );
      const flag =
        (domleft === emptyleft && yDis === parseInt(height)) ||
        (domtop === emptytop && xDis === parseInt(width));
      return flag;
    },
    /**
     * 实现的功能主要是判断当前是否已经成功
     *
     */
    chackWin() {
      const blockDomArr = this.$refs.block;
      return blockDomArr.every(dom => {
        const { left: domLeft, top: domTop } = dom.style;
        const { correctx: correctX, correcty: correctY } = dom.dataset;
        const flag =
          parseInt(domLeft) === parseInt(correctX) &&
          parseInt(domTop) === parseInt(correctY);
        return flag;
      });
    },
    /**
     * 成功之后执行的方法
     */
    winGame(emptyDom) {
      setTimeout(() => {
        alert("成功");
        emptyDom.style.opacity = 1;
        setTimeout(() => {
          this.goToNextLeavel();
        }, 300);
      }, 300);
    },
    /**
     * 返回第一关的方法
     */
    goToNextLeavel() {
      console.log("213");
      const answerFlag = window.confirm("要玩下一关嘛?");
      if (answerFlag) {
        this.$emit("next");
      }
    }
  },
  computed: {
    // 计算当前图片的宽度
    blockWidth() {
      return this.width / this.col;
    },
    // 计算当前图片的高度
    blockHeight() {
      return this.height / this.row;
    },
    // 拿到全部的图片
    correctPoints() {
      const { row, col, blockWidth, blockHeight } = this; //es6语法,获取到this中的row,col
      const arr = [];
      for (let i = 0; i < row; i++) {
        for (let j = 0; j < row; j++) {
          arr.push({
            x: j * blockWidth,
            y: i * blockHeight,
            id: new Date().getTime() + Math.random() * 100
          });
        }
      }
      return arr;
    },
    // 返回打乱之后的图片
    blockPoints() {
      const points = this.correctPoints;
      const length = points.length;
      const lastEle = points[length - 1];
      const newArr = [...points];
      newArr.length = length - 1;
      newArr.sort(() => Math.random() - 0.5);
      newArr.push(lastEle);
      return newArr;
    }
  }
};
</script>

<style>
.puzzle {
  position: relative;
  border: 1px solid #ccc;
}
.puzzle__block {
  box-sizing: border-box;
  position: absolute;
  transition: all 0.3s;
  border: 2px solid #eeeeee;
}
</style>

这里需要注意一点,获取图片的时候,需要将图片文件放在public文件夹中,不允许直接放在外面,因为这里获取图片的时候使用的是绝对路径,也就是 8080/~ 。使用相对地址获取不到。
在这里插入图片描述

### 关于 Vue 开发拖拽拼图小游戏 Vue 是一种流行的前端框架,其响应式数据绑定机制非常适合用于开发交互性强的应用程序,例如拖拽拼图小游戏。以下是关于如何使用 Vue 实现此类游戏的相关信息。 #### 数据模型与逻辑处理 在 Vue 中实现拖拽拼图的核心在于维护一个数组来表示拼图片段的位置状态,并通过事件监听器捕获用户的拖拽行为。当用户完成拖动时,可以通过修改该数组中的元素顺序来反映位置的变化[^2]。下面是一个简单的代码示例: ```javascript <template> <div class="puzzle-container"> <div v-for="(item, index) in list" :key="index" @dragstart="startDrag(index)" @drop="endDrop(index)" @dragover.prevent draggable> {{ item }} </div> </div> </template> <script> export default { data() { return { list: [1, 2, 3, 4], dragIndex: null, overindex: null, steps: 0, }; }, methods: { startDrag(index) { this.dragIndex = index; }, endDrop(index) { if (this.dragIndex === index || !this.overindex) return; // 交换列表项 [this.list[this.dragIndex], this.list[index]] = [ this.list[index], this.list[this.dragIndex], ]; this.steps += 1; // 记录步数 } } }; </script> ``` 此代码片段展示了基本的拖放功能,利用 `@dragstart` 和 `@drop` 事件分别记录起始索引和目标索引,随后调整数组内的值以同步界面变化[^2]。 #### 用户体验优化 为了提高用户体验,开发者还可以引入动画效果或者更复杂的碰撞检测算法。此外,考虑到移动端设备可能不具备标准鼠标拖拽支持的情况,建议同时兼容触摸手势操作[^1]。 #### 参考项目实例 对于希望快速上手的朋友来说,可以直接参考已有的开源项目。例如,“Vue-auto-Puzzle” 提供了一个完整的解决方案,不仅实现了基础的功能,而且还扩展到了 Weex 平台之上[^1]。该项目位于 GitCode 上,可通过链接访问具体实现细节。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值