vue实现拼图小游戏

App.vue

<template>
  <div>
    <d-puzzle v-bind="puzzleConfig[level]" :key="level" @nextLevel="goToNextLevel" @imgWHConfirm="imgWHConfirm"/>
  </div>
</template>

<script>
  import DPuzzle from './Puzzle'

  export default {
    components: {
      DPuzzle
    },
    data() {
      return {
        level: 0,
        puzzleConfig: [
          {id:1,row: 2, col: 2, img: './images/1.jpeg'},
          {row: 3, col: 3, img: './images/2.jpeg'},
          {row: 3, col: 3, img: './images/4.jpeg'},
          {row: 2, col: 2, img: './images/5.jpeg'},
          {row: 2, col: 2, img: './images/6.jpg'},
          {row: 3, col: 3, img: './images/3.jpeg'},
        ]
      }
    },
    methods: {
      goToNextLevel() {
        if (++this.level === this.puzzleConfig.length){
          let answerFlag = confirm("恭喜您,已经通过全部关卡,要重新开始嘛?")
          if (answerFlag){
            this.level = 0;
          }
        }
      },
      imgWHConfirm(){
        setTimeout(() => {
          let answerFlag = confirm("检测到该图片宽高不一致,会影响您的游戏体验,是否跳过该关卡?")
          if (answerFlag) {
            this.goToNextLevel();
          }
        },500);
      }
    }
  }
</script>

<style>

</style>

Puzzle.vue

<template>
  <div class="puzzle" :style="{width: width+'px',height: height+'px'}">
    <div class="puzzle__block"
         v-for="(item,index) in blockPoints" :key="item.id"
         @click="handleClick"
         :ref="index === blockPoints.length - 1 ? 'empty' : 'block'"
         :class="index === blockPoints.length - 1 ? 'empty-block' : 'img-block'"
         :data-correctX="correctPoints[index].x"
         :data-correctY="correctPoints[index].y"
         :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`,
           backgroundSize: `${width}px ${height}px`,
         }"></div>
  </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
      }
    },
    created() {
      this.getImgWidthHeight(this.img).then(res => {
        if (res.success) {
          let wh = res.data;
          const {width, height} = wh;
          if (width != height) {
            this.$emit('imgWHConfirm');
          }
        } else {
          alert(res.msg);
        }
      })
    },
    computed: {
      blockWidth() {
        return this.width / this.col;
      },
      blockHeight() {
        return this.height / this.row;
      },
      correctPoints() {
        const {row, col, blockWidth, blockHeight} = this;
        let arr = [];
        for (let i = 0; i < row; i++) {
          for (let j = 0; j < col; j++) {
            arr.push({
              id: this.randomStr(2) + Math.random() + +new Date(),
              x: j * blockWidth,
              y: i * blockHeight
            })
          }
        }
        return arr;
      },
      blockPoints() {
        let points = this.correctPoints;
        let length = points.length;
        let lastEle = points[length - 1];
        const copyOfPoints = [...points];
        copyOfPoints.length = length - 1;
        let shuffledPoints = copyOfPoints.sort(() => Math.random() - 0.5);
        shuffledPoints.push(lastEle);
        return shuffledPoints;
      }
    },
    data() {
      return {
        randomSeed: ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm',
          'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 'C', 'V', 'B', 'N', 'M',
          '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
        isStopPlay: false
      }
    },
    methods: {
      async getImgWidthHeight(src) {
        let result = {};
        function imgLoad(src, result) {
          let img = new Image();
          img.src = src;
          return new Promise(resolve => {
            if (img.complete) {
              result.success = true;
              result.data = {
                width: img.width,
                height: img.height
              }
              resolve();
            } else {
              let timeOut = setTimeout(() => {
                result.success = false;
                result.msg = "图片加载失败";
                resolve();
              }, 2000);//2000ms可以认为是超时时间
              img.onload = function () {
                result.success = true;
                result.data = {
                  width: img.width,
                  height: img.height
                }
                window.clearTimeout(timeOut);
                resolve();
              }
            }
          })
        }

        await imgLoad(src, result);
        return result;
      },
      randomStr(len) {
        let arr = this.randomSeed;
        let result = '';
        for (let i = 0; i < len; i++) {
          result += arr[Math.floor(Math.random() * arr.length)];
        }
        return result;
      },
      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;
        if (this.isVictory()) {
          this.winGame(emptyDom);
        }
      },
      isAdjacent(blockDom, emptyDom) {
        const {left: blockLeft, top: blockTop} = blockDom.style;
        const {left: emptyLeft, top: emptyTop} = emptyDom.style;
        const {blockWidth, blockHeight} = this;
        const xDis = Math.floor(Math.abs(parseFloat(blockLeft) - parseFloat(emptyLeft)));
        const yDis = Math.floor(Math.abs(parseFloat(blockTop) - parseFloat(emptyTop)));
        return (blockLeft === emptyLeft && yDis === parseInt(blockWidth))
          || (blockTop === emptyTop && xDis === parseInt(blockHeight));
      },
      isVictory() {
        const blockDomArr = this.$refs.block;
        return blockDomArr.every(dom => {
          const {left: domLeft, top: domTop} = dom.style;
          const {correctx: correctX, correcty: correctY} = dom.dataset;
          return parseInt(domLeft) === parseInt(correctX) && parseInt(domTop) === parseInt(correctY);
        })
      },
      winGame(emptyDom) {
        emptyDom.style.opacity = "1";
        setTimeout(() => {
          alert("恭喜您,拼图成功!");
          setTimeout(() => {
            const answerFlag = confirm("要玩下一关嘛?")
            if (answerFlag) {
              this.$emit("nextLevel");
            } else {
              this.isStopPlay = true;
            }
          }, 300);
        }, 500);
      }
    }
  }
</script>

<style>
  .puzzle {
    position: relative;
    border: 2px solid #ccc;
  }

  .puzzle__block {
    box-sizing: border-box;
    position: absolute;
    transition: all .3s;
    border: 1px solid #fff;
  }

  .puzzle__block.img-block {
    cursor: pointer;
  }

  .puzzle__block.empty-block {
    opacity: 0;
  }
</style>

源码和图片:
https://gitee.com/luminescent-acridine/vue-puzzle-game.git

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值