基于canvas+uniapp的9宫格拼图游戏组件

基于 canvas+uniapp 的 9 宫格拼图游戏

涉及到的 canvas 基础知识

  • 创建画布

    <canvas id="’c1’"></canvas>
    
  • 获取画笔

    let context = uni.createCanvasContext(‘c1’)
    
  • 绘图

    /**
     * img原图片
     * x1,y1,w1,w2 从图片指定位置开始截取指定大小的图块
     * x2,y2,w2,h2 将截取的图片放置到画布指定位置,并设置大小
     */
    drawImage(img, x1, y1, w1, h1, x2, y2, w2, h2);
    
  • 清除画布

    /**
     * x,y 指定开始清除的位置
     * w,h 清除区域宽高
     */
    clearRect(x, y, w, h);
    

其他知识

  • 获取 canvas 画布宽高,用于计算拼图图块大小

    uni
      .createSelectorQuery()
      .in(this)
      .select('.canvas')
      .boundingClientRect()
      .exec(res => {
        if (res[0]) {
          // res[0].width;
          // res[0].height;
        }
      });
    
  • 使用二维数组定义 9 宫格图块位置

    num = [
      [1, 2, 3],
      [10, 11, 12],
      [20, 21, 22],
    ];
    
  • 为保证拼图的随机性,需要将图块位置随机打乱,并定义标号为 22 的图块为空白图块,且始终保持在右下角,即 9 宫格游戏的第 9 个位置

  • 移动滑块

    • 当点击图块为空白图块相邻的图块时,交换两个图块的位置,即交换二维数组中对应的标号
    • 位置更新后,重新绘制图块
  • 判断游戏是否完成

    • 每次交换完图块后,使用双层 for 循环,遍历 num 中数据,判断游戏是否完成
    //判断游戏是否完成(每个图块位置顺序与num初始值相同即为完成拼图)
    checkWin() {
      let num = this.num;
      for (var i = 0; i < 3; i++) {
        for (var j = 0; j < 3; j++) {
          if (num[i][j] !== Number(i + '' + j)) {
            return false;
          }
        }
      }
      return true;
    }
    

整体代码

<template>
  <view style="width: 100%; height: 100%">
    <canvas class="canvas" canvas-id="canvasId" @touchend="touchend" id="canvasId"></canvas>
  </view>
</template>
<script>
export default {
  name: 'Jigsaw',
  props: ['url'],
  data() {
    return {
      context: '',
      isDone: false, //是否完成拼图
      canvasWidth: 0, //画布宽度
      canvasHeight: 0, //画布高度
      num: [
        [0, 1, 2], //[00, 01, 02],
        [10, 11, 12],
        [20, 21, 22],
      ], //定义方块初始位置 22为空白图块
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      // 创建绘图对象
      let context = uni.createCanvasContext('canvasId');
      this.context = context;
      //获取画布大小
      uni
        .createSelectorQuery()
        .in(this)
        .select('.canvas')
        .boundingClientRect()
        .exec(res => {
          if (res[0]) {
            this.canvasWidth = res[0].width;
            this.canvasHeight = res[0].height;
            //画布大小获取成功后再画图
            this.generateNum();
            this.drawCanvas();
          }
        });
    },
    //打乱图块位置
    generateNum() {
      for (var i = 0; i < 50; i++) {
        //随机抽取其中一个数据
        var i1 = Math.round(Math.random() * 2);
        var j1 = Math.round(Math.random() * 2);
        //再随机抽取其中一个数据
        var i2 = Math.round(Math.random() * 2);
        var j2 = Math.round(Math.random() * 2);
        //最后一个图块位置不变
        if ((i1 == 2 && j1 == 2) || (i2 == 2 && j2 == 2)) {
          continue;
        }
        let tempNum = [].concat(this.num);
        var temp = tempNum[i1][j1];
        tempNum[i1][j1] = tempNum[i2][j2];
        tempNum[i2][j2] = temp;
        this.num = tempNum;
      }
    },

    //根据num图块位置,画图出拼图
    drawCanvas() {
      //远程图片需加载至本地
      uni.getImageInfo({
        src: this.url,
        success: img => {
          //清空画布
          this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
          //画布宽高1/3 用于计算每个图块的位置及宽高
          let cw = this.canvasWidth / 3;
          let ch = this.canvasHeight / 3;
          //图片宽高1/3 用于计算每个图块的位置及宽高
          let iw = img.width / 3;
          let ih = img.height / 3;
          //使用双重for循环绘制3x3的拼图
          for (var i = 0; i < 3; i++) {
            for (var j = 0; j < 3; j++) {
              if (this.num[i][j] != 22) {
                //获取数值的十位数,即第几行
                var row = parseInt(this.num[i][j] / 10);
                //获取数组的个位数,即第几列
                var col = this.num[i][j] % 10;
                //在画布的相关位置上绘图
                this.context.drawImage(
                  img.path,
                  col * iw,
                  row * ih,
                  iw,
                  ih,
                  j * cw,
                  i * ch,
                  cw,
                  ch
                );
              }
            }
          }
          this.context.draw(true);
        },
        fail(err) {
          console.log(err);
        },
      });
    },
    touchend(e) {
      if (this.isDone) {
        this.$u.toast('游戏已完成');
        return;
      }
      let x = e.changedTouches[0].x;
      let y = e.changedTouches[0].y;
      let w = this.canvasWidth / 3;
      var row = parseInt(y / w); //将x和y换算成几行几列
      var col = parseInt(x / w);
      if (this.num[row][col] != 22) {
        //如果当前点击的不是空白区域
        this.detectBox(row, col); //移动点击的方块
        this.drawCanvas(); //重新绘制画布
        this.isDone = this.checkWin(); //检查游戏是否成功
        if (this.isDone) {
          this.$emit('callback');
        }
      }
    },
    //判断游戏是否完成(每个图块位置顺序与num初始值相同即为完成拼图)
    checkWin() {
      let num = this.num;
      for (var i = 0; i < 3; i++) {
        for (var j = 0; j < 3; j++) {
          if (num[i][j] !== Number(i + '' + j)) {
            return false;
          }
        }
      }
      return true;
    },
    detectBox(i, j) {
      //如果点击的方块不在最上面一行
      if (i > 0) {
        //检测空白区域是否在当前方块的正上方
        if (this.num[i - 1][j] == 22) {
          //交换空白区域与当前方块的位置
          this.num[i - 1][j] = this.num[i][j];
          this.num[i][j] = 22;
          return;
        }
      }
      //如果点击的方块不在最下面一行
      if (i < 2) {
        //检测空白区域是否在当前方块的正下方
        if (this.num[i + 1][j] == 22) {
          //交换空白区域与当前方块的位置
          this.num[i + 1][j] = this.num[i][j];
          this.num[i][j] = 22;
          return;
        }
      }
      //如果点击的方块不在最左边一列
      if (j > 0) {
        //检测空白区域是否在当前方块的左边
        if (this.num[i][j - 1] == 22) {
          //交换空白区域与当前方块的位置
          this.num[i][j - 1] = this.num[i][j];
          this.num[i][j] = 22;
          return;
        }
      }
      //如果点击的方块不在最右边一列
      if (j < 2) {
        //检测空白区域是否在当前方块的右边
        if (this.num[i][j + 1] == 22) {
          //交换空白区域与当前方块的位置
          this.num[i][j + 1] = this.num[i][j];
          this.num[i][j] = 22;
          return;
        }
      }
    },
  },
};
</script>
<style lang="scss" scoped>
.canvas {
  height: 100%;
  width: 100%;
}
</style>

原文:https://blog.csdn.net/qq_38545425/article/details/128221249

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值