拼拼乐小游戏开发(VUE版本)

深秋的某一天,在地铁上面刷着手机,看到徐小夕的公众号推送的一篇文章,介绍的是一个移动端端的拼图小游戏,于是自己尝试着识别二维码完了下,感觉还挺有意思的.周末抽空研究了一下,看了下这个小游戏有哪些功能.问了下原作者,他用的是原生JS写的一版.自己本身在学习使用VUE开发项目,于是便萌发了用VUE尝试改版一下.正好可以学习学习一下.于是看了下游戏的大体效果,决定尝试用VUE改写一版玩一下,说干就干.也欢迎有兴趣,有想法的小伙伴可以一起讨论.

下面列一下用到的一些知识点:

  • 洗牌算法
  • CSS3动画切换(图片切割样式定位)
  • 用FileReader API实现本地预览文件
  • 用Canvas生成海报
  • webpack打包(不同难度等级切换样式问题)

零零碎碎的花了2~3个周末的时间,然后部署了下页面.虽然存在一些问题,代码也比较粗糙.但是还是实现了自己想要的效果.

大致的实现思路可以参考趣谈前端公众号里面的文章,教你用200行代码写一个爱豆拼拼乐H5小游戏(附源码).这里我对这个游戏VUE实现了相关的功能.而且还增加了一些自己的想法.

主要有相邻元素才可以挪动难度等级选择,发版的是两个不同的版本.当然在实现的过程中也遇到了一些问题.不过经过自己的折腾还是基本实现出来自己想要的效果啦.

游戏界面
等级选择
挑战结果

先来看看大致的实现功能模块:

功能模块流程图

具体实现:

1.根据选择的难度等级生成n*n的矩阵图切割图片

// 生成n维矩阵
generateMatrix(n, dx, dy) {
  var arr = [],
    index = 0;
  for (var i = 0; i < n; i++) {
    for (var j = 0; j < n; j++) {
      arr.push({ x: j * dx, y: i * dy, index: index });
      index++;
    }
  }
  return arr;
}

2.数组乱序(重新排列)

    upsetArr(arr) {
      return arr.sort(function() {
        return Math.random() > 0.5 ? -1 : 1;
      });
    }

3.点击高亮并且切换对应的位置(当前点击的和你要交换的)

   // 点击高亮并且切换对应位置 (想办法交换对应索引位置的x,y值即可)
    changePositon(e, item) {
      //点击小图片切换位置方法
      let reg = /active/g;
      this.boxArractivelass = item;
      let pieces = document.querySelectorAll(".piece");
      if (!this.wall) {
        this.wall = 1;
        this.prevEl = e.target;
        for (var i = 0, len = pieces.length; i < len; i++) {
          // 使用replace为了避免元素后期加入其他类名
          pieces[i].className = pieces[i].className.replace(" active", "");
        }
        !reg.test(this.className) && (this.className += " active");
      } else {
        this.wall = 0;
        var prevIndex = +this.prevEl.getAttribute("index"),
          curIndex = +e.target.getAttribute("index");

        // 置换数组
        this.swap(this.pool, prevIndex, curIndex);
        this.prevEl.style.transform =
          "translate(" +
          this.pool[prevIndex].x +
          "vw," +
          this.pool[prevIndex].y +
          "vh" +
          ")";
        e.target.style.transform =
          "translate(" +
          this.pool[curIndex].x +
          "vw," +
          this.pool[curIndex].y +
          "vh" +
          ")";
        // 清除样式
        this.boxArractivelass = -1;

        // 校验是否成功
        if (this.isTestSuccess(this.pool)) {
    
          clearInterval(this.timer);
          this.startDx -= 100;
          this.issuccess = true;
          this.transformX(this.$refs.wrap, this.startDx + "vw");
       
        }
      }
    }

4.点击交换图片的位置

    // 置换数组(对应索引的x,y值进行交换)
    swap(arr, indexA, indexB) {
      // ES6的解耦交换方式: [arr[index], arr[n]] = [arr[n], arr[index]];
      [arr[indexA], arr[indexB]] = [arr[indexB], arr[indexA]];
    },

5.检验是否成功方法:

    //对比数组中的每一个值是否与之前的值相等
    isTestSuccess(arr) {
      return arr.every(function(item, i) {
        return item.index === i;
      });
    }

在实现的过程中,遇到一个印象比较深的问题就是,当在第一个页面选择简单等级,然后点击开始游戏,然后返回重新选择中级等级,由于样式加载顺序,导致页面展示的还是之前简单等级的页面样式.

之前出现这种问题的原因是由于,我这边是通过判断选择等级,然后import引入不同的样式文件导致的.

例如:实现的思路判断:

if(this.gradeSelected==3) {
    import('../assets/style/default.css');
} else if(this.gradeSelected===4){
    import('../assets/style/middle.css');
} else if(this.gradeSelected===5) {
    import('../assets/style/senior.css');
}
打包样式问题

后来重新换了一种思路,直接写了一个方法,本地存储选中的等级,用来设置选中不同难度等级的样式文件.然后动态的插入到head标签中.

    // 引入设置和获取本地存储的挑战等级标识
    import localStorage from "./storage"; 
    const skin = {};
    let getSkinStyle = (skin) => {
    if (!skin) {
        return "";
    }
    if (skin === 'three') {
        return `简单等级的样式文件`
    } else if(skin==='four') {
        return `中等等级的样式文件`
    } else if(skin==='five') {
        return `高级等级的样式文件`
    }
    
    let setSkinStyle = (skin) => {
    let styleText = getSkinStyle(skin);
    let oldStyle = document.getElementById("skin");
    const style = document.createElement("style");
    style.id = "skin";
    style.type = "text/css";
    style.innerHTML = styleText;
    oldStyle ? document.head.replaceChild(style, oldStyle) : document.head.appendChild(style);
};
// 设置不同等级图片切割样式
skin[localStorage.getSkin()] && setSkinStyle(skin[localStorage.getSkin()]);
export { skin, setSkinStyle }

至此,现在不同等级的选择切换,再也不会造成选择等级与图片分割界面样式不符啦.也可以愉快的玩耍啦.

当然代码还是比较粗糙,也存在一些问题的.还有就是不同版本的手机也没有经过兼容性测试,代码都在一个页面,也没有进行合理的组件拆分等等.

以上游戏纯属娱乐,有兴趣的小伙伴也欢迎交流,有什么问题也可以一起探讨,源码地址也放到了我的Github上面,欢迎大家一起交流探讨,共同进步.

Github项目地址:https://github.com/pybyongbo/vue-pinpngle