Vue3+Canvas实现俄罗斯方块

7 篇文章 0 订阅
5 篇文章 0 订阅

1,封面(操作说明)

 2,运行效果

 3,index.vue

<template>
  <div class="myDiagram">
    <Elsfk></Elsfk>
  </div>
</template>
<script setup>
import Elsfk from "./elsfk.vue";
</script>
<style lang="less" scoped></style>

4,elsfk.vue

//elsfk.vue
<template>
  <div id="zyq-elsfk" class="zyq-diagram-elsfk">
    <canvas
      id="elsfk-canvas"
      ref="canvasDom"
      :width="canvasSize.width"
      :height="canvasSize.height"
    ></canvas>
  </div>
</template>
<script setup>
import {
  ref,
  reactive,
  defineProps,
  toRefs,
  computed,
  onMounted,
  nextTick,
  onBeforeMount,
  watch,
} from "vue";
const props = defineProps({
  //背景色,默认黑色
  bgColor: {
    type: String,
    default: "black",
  },
  //小方块的尺寸
  blockSize: {
    type: Number,
    default: 20,
  },
  //小方块的颜色
  blockColor: {
    type: String,
    default: "yellow",
  },
  //横向小方块的个数
  blockNumberX: {
    type: Number,
    default: 12,
  },
  //纵向小方块的个数
  blockNumberY: {
    type: Number,
    default: 20,
  },
  //小方块边框的颜色
  blockBorder: {
    type: String,
    default: "black",
  },
  //通关积分数目
  clearanceScore: {
    type: Number,
    default: 100,
  },
  //文字颜色
  fontColor: {
    type: String,
    default: "black",
  },
  //得分文字颜色
  scoreColor: {
    type: String,
    default: "#93f393",
  },
  //画布外边距
  outdager: {
    type: Number,
    default: 20,
  },
});

const {
  bgColor,
  blockSize,
  blockColor,
  blockNumberX,
  blockNumberY,
  clearanceScore,
  outdager,
  fontColor,
  scoreColor,
  blockBorder,
} = toRefs(props);

const canvasDom = ref();
//默认得分板块高度
const baseScore = ref(30);
//横向小方块的数目,至少要10个
const xNumber = computed(() => {
  return blockNumberX.value > 10 ? blockNumberX.value : 10;
});
//纵向小方块的数目,至少要20个
const yNumber = computed(() => {
  return blockNumberY.value > 20 ? blockNumberY.value : 20;
});
//requestAnimationFram 标识
const myrequest = ref();
//当前下落方块种类下标,及变形下标
const blockIndex = reactive({
  first: 0, //models数组一级索引
  second: 0, //models数组二级索引
});
//下落方块矩阵在画图矩阵中左下角的坐标点
//axisX取值范围[0,xNumber-models[blockIndex.first].length-1],
//axisY的取值范围[0,yNumber-1]
const blockAxis = reactive({
  axisX: 0,
  axisY: -1,
});
//得分与内容之间的垂直间隙
const yInterval = ref(10);
const canvasSize = reactive({
  width: 0,
  height: 0,
});
const state = reactive({
  //用户得分
  score: 0,
  //外边框颜色
  outBorderColor: "#f77979",
  //文字尺寸
  fontSize: 14,
  //文字字体
  fontFamily: "Microsoft YaHei",
});
//elsfk 主体绘制数组:每一方块矩阵彻底下落前的镜像矩阵
const dataSource = ref(
  new Array(yNumber.value).fill(new Array(xNumber.value).fill(0))
);
//elsfk 当生成新的方块之后下落计算用于绘制的矩阵
const drawSource = ref();

//作为requestAnimationFram 的时间计数器
const flag = ref(0);

//用于表示当前游戏是否开始
const ifStart = ref(false);

//用于表示当前游戏是暂停还是运行状态
const ifContinue = ref(false);

//当前选择的moels中元素矩阵的有效起始左侧下标
const realLeftStartIndex = computed(() => {
  for (
    let i = 0;
    i < models[blockIndex.first][blockIndex.second][0].length;
    i++
  ) {
    for (
      let j = 0;
      j < models[blockIndex.first][blockIndex.second].length;
      j++
    ) {
      if (models[blockIndex.first][blockIndex.second][j][i] === 1) {
        return i;
      }
    }
  }
  return 0;
});

//当前选择的models中元素矩阵的有效起始右侧下标
const realLeftEndIndex = computed(() => {
  for (
    let i = models[blockIndex.first][blockIndex.second][0].length - 1;
    i >= 0;
    i--
  ) {
    for (
      let j = 0;
      j < models[blockIndex.first][blockIndex.second].length;
      j++
    ) {
      if (models[blockIndex.first][blockIndex.second][j][i] === 1) {
        return i;
      }
    }
  }
  return models[blockIndex.first][blockIndex.second][0].length - 1;
});

//当前选择的Models中的元素矩阵有效起始上侧坐标
const realUpStartIndex = computed(() => {
  for (let i = 0; i < models[blockIndex.first][blockIndex.second].length; i++) {
    for (
      let j = 0;
      j < models[blockIndex.first][blockIndex.second][0].length;
      j++
    ) {
      if (models[blockIndex.first][blockIndex.second][i][j] === 1) {
        return i;
      }
    }
  }
  return 0;
});

//操作钥匙,值为true时可以进行左移、右移、变形操作,否则不可操作
const operatorKey = ref(true);

//延时关闭操作计时器
const closeKeyTimer = ref(0);

//该批次临时得分
const tempScore = ref(0);

//该批次得分数组下标
const tempIndexArray = ref([]);

//加速装置,分数每加5,下落以及生成小方块的速度减少1/60秒
const accelerationDevice = computed(() => {
  if (state.score > 0) {
    return Math.floor(state.score / 5);
  }
  return 0;
});
//每一个动作的标准时间单位1/60(s),也就是requestAnimationFram执行一次所需的时间,60代表1s
const oneOperationStandardTime = ref(60);

const operatorInfo = ref([
  {
    key: "左箭头",
    info: "控制小方块左移",
  },
  {
    key: "下箭头",
    info: "控制小方块加速下移",
  },
  {
    key: "右箭头",
    info: "控制小方块右移",
  },
  {
    key: "X按键",
    info: "控制小方块变形",
  },
  {
    key: "B按键",
    info: "开始游戏",
  },
  {
    key: "S按键",
    info: "暂停游戏",
  },
  {
    key: "C按键",
    info: "继续游戏",
  },
  {
    key: "E按键",
    info: "关闭游戏",
  },
]);

//绘制游戏失败信息
const gameoverInfo = reactive({
  info: "GAMEOVER",
  color: "red",
  fontSize: 25,
});

//绘制游戏胜利信息
const victoryInfo = reactive({
  info: "VICTORY",
  color: "red",
  fontSize: 30,
});

//创建记录所有方块类型的四维数组
//变形规则,矩阵转置
const models = [
  [
    [
      [1, 0, 0],
      [1, 0, 0],
      [1, 1, 0],
    ],
    [
      [0, 0, 0],
      [0, 0, 1],
      [1, 1, 1],
    ],
    [
      [0, 1, 1],
      [0, 0, 1],
      [0, 0, 1],
    ],
    [
      [1, 1, 1],
      [1, 0, 0],
      [0, 0, 0],
    ],
  ],
  [
    [
      [0, 1, 0],
      [0, 1, 0],
      [1, 1, 0],
    ],
    [
      [0, 0, 0],
      [1, 1, 1],
      [0, 0, 1],
    ],
    [
      [0, 1, 1],
      [0, 1, 0],
      [0, 1, 0],
    ],
    [
      [1, 0, 0],
      [1, 1, 1],
      [0, 0, 0],
    ],
  ],
  [
    [
      [0, 1, 0],
      [1, 1, 0],
      [0, 1, 0],
    ],
    [
      [0, 0, 0],
      [1, 1, 1],
      [0, 1, 0],
    ],
    [
      [0, 1, 0],
      [0, 1, 1],
      [0, 1, 0],
    ],
    [
      [0, 1, 0],
      [1, 1, 1],
      [0, 0, 0],
    ],
  ],
  [
    [
      [0, 1, 0],
      [0, 1, 0],
      [0, 1, 0],
    ],
    [
      [0, 0, 0],
      [1, 1, 1],
      [0, 0, 0],
    ],
  ],
  [
    [
      [1, 1],
      [1, 1],
    ],
  ],
];
//当前选择的models中元素矩阵的有效终止下册下标
const getRealBottomEndIndex = (second) => {
  for (let i = models[blockIndex.first][second].length - 1; i >= 0; i--) {
    for (let j = 0; j < models[blockIndex.first][second][i].length; j++) {
      if (models[blockIndex.first][second][i][j] === 1) {
        return i;
      }
    }
  }
  return models[blockIndex.first][second].length - 1;
};

//绘制得分板块
const drawScore = () => {
  let ctx;
  if (canvasDom.value) {
    ctx = canvasDom.value.getContext("2d");
    ctx.lineWidth = 1;
    //每次绘制之前,先清理一下绘制区域
    ctx.clearRect(
      outdager.value,
      outdager.value,
      blockSize.value * xNumber.value,
      baseScore.value
    );
    ctx.strokeStyle = state.outBorderColor;
    //绘制得分区域边框
    ctx.strokeRect(
      outdager.value,
      outdager.value,
      blockSize.value * xNumber.value,
      baseScore.value
    );
    //绘制得分文字
    ctx.font = "normal " + state.fontSize + "px " + state.fontFamily;
    ctx.fillStyle = fontColor.value;
    ctx.fillText(
      "总分:",
      outdager.value + 5,
      outdager.value + state.fontSize + 5
    );
    ctx.fillStyle = scoreColor.value;
    ctx.fillText(
      state.score,
      outdager.value + 44,
      outdager.value + state.fontSize + 6
    );
  }
};

//根据矩阵绘制方块
const drawBlocks = () => {
  let ctx;
  if (canvasDom.value) {
    ctx = canvasDom.value.getContext("2d");
    ctx.lineWidth = 1;
    // ctx.strokeStyle = state.blockColor;
    //先清洗内容画布
    ctx.clearRect(
      outdager.value + 1,
      outdager.value + baseScore.value + 1,
      blockSize.value * xNumber.value - 2,
      blockSize.value * yNumber.value + baseScore.value - 2
    );
    if (ctx) {
      //绘制小方块在canvas画布中绘制起始X坐标
      //绘制小方块在canvas画布中绘制起始y坐标
      let startX = outdager.value;
      let startY = outdager.value + baseScore.value;
      for (let i = 0; i < drawSource.value.length; i++) {
        for (let j = 0; j < drawSource.value[i].length; j++) {
          //当矩阵中该坐标元素数据为1时,进行方块绘制
          startX += j * blockSize.value;
          startY += i * blockSize.value;
          if (drawSource.value[i][j] === 1) {
            // console.log("")
            ctx.fillStyle = blockColor.value;
            // ctx.strokeStyle = blockBorder.value;
            // ctx.strokeRect(startX, startY, blockSize.value, blockSize.value);
          } else {
            ctx.fillStyle = bgColor.value;
          }
          //绘制实心正方形
          ctx.fillRect(startX, startY, blockSize.value, blockSize.value);
          //绘制实心正方形边框用于区分小方块
          ctx.strokeStyle = blockBorder.value;
          ctx.strokeRect(startX, startY, blockSize.value, blockSize.value);
          startX = outdager.value;
          startY = outdager.value + baseScore.value;
        }
      }
    }
  }
};
//随机生成下落方块矩阵方法
const generateMatrix = () => {
  //共有五种方块类型,随机生成0-4随机数
  blockIndex.first = Math.floor(Math.random() * 5);
  //四种方块矩阵每种最多又有四种变形方式,虽然有的变形是一样的
  blockIndex.second = Math.floor(
    Math.random() * models[blockIndex.first].length
  );
  //随机生成方块矩阵在画图矩阵中左下角的x坐标点
  axisX取值范围[0,xNumber-models[blockIndex.first].length-1],
  blockAxis.axisX = Math.floor(
    Math.random() * (xNumber.value - models[blockIndex.first].length - 1)
  );

  blockAxis.axisY = -1;
};
//校验是否可以下落
const ifCanGoDown = () => {
  //当方块矩阵下落到最后一个位置时,也要停止下落
  //获取当矩阵元素处于最容器矩阵底部时有效下测下标同高度的差值
  let virtualDeffience =
    models[blockIndex.first][blockIndex.second].length -
    1 -
    getRealBottomEndIndex(blockIndex.second);
  if (blockAxis.axisY + 1 - virtualDeffience > yNumber.value - 1) {
    return false;
  }
  return verifyCoreLogic(
    blockAxis.axisX,
    blockAxis.axisY + 1,
    blockIndex.second
  );
};
//校验是否可以左移
const ifCanGoLeft = () => {
  //判断当方块矩阵元素至于最左边时不能进行左移
  if (blockAxis.axisX - 1 + realLeftStartIndex.value < 0) {
    return false;
  }
  return verifyCoreLogic(
    blockAxis.axisX - 1,
    blockAxis.axisY,
    blockIndex.second
  );
};
//校验是否可以右移
const ifCanGoRight = () => {
  //判断当前方块矩阵元素置于最右边时不能进行右移
  if (blockAxis.axisX + 1 + realLeftEndIndex.value > xNumber.value - 1) {
    return false;
  }
  return verifyCoreLogic(
    blockAxis.axisX + 1,
    blockAxis.axisY,
    blockIndex.second
  );
};
//校验是否可以转置
const ifCanTransposition = () => {
  //首先判断当前矩阵x,y坐标是否存在于边界之外,若存在于边界之外则不能变形
  if (
    blockAxis.axisX < 0 ||
    blockAxis.axisX +
      models[blockIndex.first][blockIndex.second][0].length -
      1 >
      xNumber.value - 1 ||
    blockAxis.axisY > yNumber.value - 1
  ) {
    return false;
  }
  //通过更改blockIndex.second的值从而达到矩阵的转置
  return verifyCoreLogic(
    blockAxis.axisX,
    blockAxis.axisY,
    (blockIndex.second + 1) % models[blockIndex.first].length
  );
};
//校验是否游戏结束
const ifGameOver = () => {
  //只要判断在停滞状态时该次矩阵方块的有效起始上侧坐标所在的位置小于0
  //则游戏结束
  let dataLength = models[blockIndex.first][blockIndex.second].length - 1;
  if (blockAxis.axisY - dataLength + realUpStartIndex.value < 0) {
    return true;
  }
  return false;
};
//校验是否得分
const ifGetScore = () => {
  //对镜像中当前矩阵最终停留所在行进行遍历计算判断全为1的行则得分
  let dataLength = models[blockIndex.first][blockIndex.second].length;
  //如果小方块下落之后的真实下标小于零则根本不存在得分的可能
  if (
    blockAxis.axisY -
      dataLength +
      1 +
      getRealBottomEndIndex(blockIndex.second) <
    0
  ) {
    return false;
  }
  //用于判断循环的某一行是否得分
  let scoreFlag = true;
  //当realBottomEndIndex小于models[blockIndex.first][blockIndex.second].length-1时
  //元素下落到最后位置,blockAxis.axisY可能超出drawSource.value.length
  if (blockAxis.axisY === yNumber.value) {
    blockAxis.axisY = yNumber.value - 1;
  }
  //计算得分起始点坐标
  let startY = blockAxis.axisY - dataLength + 1;
  if (startY < 0) start = 0;
  for (let i = startY; i <= blockAxis.axisY; i++) {
    for (let j = 0; j < drawSource.value[i].length; j++) {
      if (drawSource.value[i][j] === 0) {
        scoreFlag = false;
      }
    }
    if (scoreFlag) {
      //得一分,将该行数值清空
      tempScore.value++;
      //保存得分数组下标
      tempIndexArray.value.push(i);
    }
    scoreFlag = true;
  }
  if (tempScore.value > 0) {
    return true;
  }
  return false;
};
//清除得分行
//这里清除得分行从当前批次的绘制矩阵清除
const clearScoreInArray = () => {
  for (let i = 0; i < tempIndexArray.value.length; i++) {
    drawSource.value[tempIndexArray.value[i]] = new Array(xNumber.value).fill(
      0
    );
  }
};
//得分之后当前矩阵数据源下落,此时也是操作的绘制矩阵
const dataDrown = () => {
  if (tempIndexArray.value.length > 0) {
    for (let i = 0; i < tempIndexArray.value.length; i++) {
      drawSource.value.splice(tempIndexArray.value[i], 1);
      drawSource.value.unshift(new Array(xNumber.value).fill(0));
    }
  }
};
//校验是否可以转置、下移、左移、右移的核心对比逻辑
//即假设方块矩阵在绘制矩阵中的x,y坐标发生变化时,二者矩阵中是否存在某一位置元素之和为2
//在此不做边界坐标判断,只在上面特定方法中才进行边界判断
//x,y坐标以models矩阵左下角元素在dataSource中的坐标为准
const verifyCoreLogic = (x, y, secondIndex) => {
  //在dataSource中当前循环计算坐标的值
  let sourceValue = 0;
  //在models数组中当前矩阵最后一行循环计算位置元素的值
  let modelValue = 0;
  //models中矩阵高度-1
  let dataLength = models[blockIndex.first][secondIndex].length - 1;
  let start = 0;
  if (y < dataLength) {
    start = dataLength - y;
  }
  for (; start <= getRealBottomEndIndex(secondIndex); start++) {
    for (
      let j = 0;
      j < models[blockIndex.first][secondIndex][start].length;
      j++
    ) {
      sourceValue = dataSource.value[y - dataLength + start][x + j];
      modelValue = models[blockIndex.first][secondIndex][start][j];
      if (sourceValue + modelValue > 1) {
        return false;
      }
    }
  }
  return true;
};

//矩阵位移核心逻辑
const matrixMoveCoreLogic = () => {
  //根据当前矩阵x、y坐标替换在drawSource中的位置,然后进行绘制
  //2,更改画图数据源
  //每次执行下落逻辑之前需初始化本次镜像数组,防止下落粘连出现轨迹
  drawSource.value = JSON.parse(JSON.stringify(dataSource.value));
  //使用双层for循环
  //计算第一层矩阵纵向可绘制的长度从而计算目标矩阵第一层数组起始绘制坐标点与dataIndex.axisY的值有关
  //目标矩阵外层纵向起始绘制坐标
  let start = 0;
  let outLength = models[blockIndex.first][blockIndex.second].length;
  if (outLength > blockAxis.axisY + 1) {
    start = outLength - blockAxis.axisY - 1;
  }

  for (; start <= getRealBottomEndIndex(blockIndex.second); start++) {
    for (let j = 0; j <= realLeftEndIndex.value; j++) {
      //循环替换元素
      //初始行数  y-length+1
      drawSource.value[blockAxis.axisY + start - outLength + 1][
        blockAxis.axisX + j
      ] =
        models[blockIndex.first][blockIndex.second][start][j] +
        drawSource.value[blockAxis.axisY + start - outLength + 1][
          blockAxis.axisX + j
        ];
    }
  }

  //执行绘制逻辑
  drawBlocks();
};
//绘制内容部分
const drawContent = () => {
  flag.value++;
  //当flag==1时,生成方块矩阵数组
  if (flag.value === 1) {
    //初始化方块矩阵
    generateMatrix();
    //更新绘制矩阵
    drawSource.value = JSON.parse(JSON.stringify(dataSource.value));
    //打开操作开关
    operatorKey.value = true;
    //重置计时器
    closeKeyTimer.value = 0;
  }

  if (
    flag.value > oneOperationStandardTime.value - accelerationDevice.value &&
    (flag.value % oneOperationStandardTime.value) - accelerationDevice.value ===
      1
  ) {
    //每两秒执行一次下落操作,requestAnimation的执行时间间隔是1000/60,故每120次是两秒
    //判断是否可以下落
    if (ifCanGoDown() && tempScore.value === 0) {
      //执行下落逻辑
      //1,更改矩阵Y轴坐标
      blockAxis.axisY++;
      matrixMoveCoreLogic();
      myrequest.value = requestAnimationFrame(drawContent);
    } else {
      //1s内允许变化,1s后关闭操作开关
      closeKeyTimer.value++;
      if (closeKeyTimer.value === 1) {
        myrequest.value = requestAnimationFrame(drawContent);
      }
      if (closeKeyTimer.value === 2) {
        operatorKey.value = false;
        //校验是否得分
        if (ifGetScore()) {
          //将当前得分计入总分,并重新绘制得分板块
          state.score += tempScore.value;
          drawScore();
          //判断游戏是否通关
          if (state.score >= clearanceScore.value) {
            //游戏通关,则游戏结束
            endGameLogic();
            drawResult(victoryInfo);
          } else {
            //未通关
            //如果存在得分,则将得分行数据清除,绘制一遍
            clearScoreInArray();
            drawBlocks();
            myrequest.value = requestAnimationFrame(drawContent);
          }
          //若未得分再判断游戏是否结束
        } else if (ifGameOver()) {
          endGameLogic();
          drawResult(gameoverInfo);
        } else {
          flag.value = 0;
          //未得分游戏未结束
          tempScore.value = 0;
          tempIndexArray.value = [];
          closeKeyTimer.value = 0;
          //并将该方块矩阵的位置永久存档
          dataSource.value = JSON.parse(JSON.stringify(drawSource.value));
          myrequest.value = requestAnimationFrame(drawContent);
        }
      }
      if (closeKeyTimer.value === 3 && tempScore.value > 0) {
        //若存在得分则将元素下移,再绘制
        //得分状态,元素下移
        dataDrown();
        drawBlocks();
        //并将该方块矩阵的位置永久存档
        dataSource.value = JSON.parse(JSON.stringify(drawSource.value));
        myrequest.value = requestAnimationFrame(drawContent);
      }
      if (closeKeyTimer.value === 4 && tempScore.value > 0) {
        tempScore.value = 0;
        tempIndexArray.value = [];
        //第四次进入的时候,为得分情况,第三次进入时未初始化计数器,该次初始化计数器
        flag.value = 0;
        closeKeyTimer.value = 0;
        myrequest.value = requestAnimationFrame(drawContent);
      }
    }
  } else {
    myrequest.value = requestAnimationFrame(drawContent);
  }
};
//绘制游戏开始之前操作说明
const drawOperatorInfo = () => {
  let ctx;
  if (canvasDom.value) {
    ctx = canvasDom.value.getContext("2d");
    ctx.font = "normal " + state.fontSize + "px " + state.fontFamily;
    ctx.fillStyle = fontColor.value;
    for (let i = 0; i < operatorInfo.value.length; i++) {
      ctx.fillText(
        operatorInfo.value[i].key + " : " + operatorInfo.value[i].info,
        outdager.value + 10,
        outdager.value +
          baseScore.value +
          state.fontSize * (i + 1) +
          10 * (i + 1)
      );
    }
  }
};

//绘制游戏结果
const drawResult = (victoryInfo) => {
  let ctx;
  if (canvasDom.value) {
    ctx = canvasDom.value.getContext("2d");
    //清空画布,绘制外层边框
    drawOuterBorder();
    //绘制文字
    ctx.fillStyle = victoryInfo.color;
    ctx.font = "normal " + victoryInfo.fontSize + "px " + state.fontFamily;
    ctx.fillText(victoryInfo.info, 70, 150);
  }
};
//游戏结束所执行的逻辑
const endGameLogic = () => {
  ifStart.value = false;
  ifContinue.value = true;
  cancelAnimationFrame(myrequest.value);
  flag.value = 0;
  tempScore.value = 0;
  tempIndexArray.value = [];
  state.score = 0;
  operatorKey.value = false;
  closeKeyTimer.value = 0;
  blockAxis.axisX = 0;
  blockAxis.axisY = -1;
  blockIndex.first = 0;
  blockIndex.second = 0;
  dataSource.value = new Array(yNumber.value).fill(
    new Array(xNumber.value).fill(0)
  );
  drawSource.value = dataSource.value;
};
//监听键盘事件
const keyBoardOperator = (e) => {
  e.preventDefault();
  switch (e.keyCode) {
    //<-左箭头触发方法
    //左移校验,左移
    case 37:
      if (operatorKey.value && ifCanGoLeft()) {
        blockAxis.axisX--;
        matrixMoveCoreLogic();
      }
      break;
    //下箭头触发方法
    //下移校验,下移,重绘
    case 40:
      if (operatorKey.value && ifCanGoDown()) {
        blockAxis.axisY++;
        matrixMoveCoreLogic();
      }
      break;
    //右箭头触发方法
    //右移校验,右移,重绘
    case 39:
      if (operatorKey.value && ifCanGoRight()) {
        blockAxis.axisX++;
        matrixMoveCoreLogic();
      }
      break;
    //矩阵转置校验,矩阵转置,重绘,按键x触发方法
    case 88:
      if (operatorKey.value && ifCanTransposition()) {
        blockIndex.second =
          (blockIndex.second + 1) % models[blockIndex.first].length;
        matrixMoveCoreLogic();
      }
      break;
    //暂停s,
    case 83:
      //当游戏处于开始状态时,运行状态时将其改为暂停状态,且不可操作
      if (ifStart.value && ifContinue.value) {
        ifContinue.value = false;
        cancelAnimationFrame(myrequest.value);
        operatorKey.value = false;
      }
      break;
    //继续 c
    case 67:
      //当游戏处于开始状态,暂停状态时将其更改为运行状态
      if (ifStart.value && !ifContinue.value) {
        ifContinue.value = true;
        operatorKey.value = true;
        myrequest.value = requestAnimationFrame(drawContent);
      }
      break;
    //关闭e
    case 69:
      //停止游戏,清空分数,临时分数,保存临时分数位置,重置数据源
      //当游戏处于开始状态时才能关闭
      if (ifStart.value) {
        endGameLogic();
        drawDiagram();
      }
      break;
    //开始 b
    case 66:
      //当游戏处于结束状态时将其改为开始状态,默认为运行状态
      if (!ifStart.value) {
        ifStart.value = true;
        ifContinue.value = true;
        drawContent();
        drawScore();
      }
      break;
    default:
  }
};
//绘制外层边框
const drawOuterBorder = () => {
  let ctx;
  if (canvasDom.value) {
    ctx = canvasDom.value.getContext("2d");
    //清除上次绘制
    ctx.clearRect(
      outdager.value - 2,
      outdager.value,
      blockSize.value * xNumber.value + 4,
      canvasSize.height - outdager.value * 2
    );
    ctx.lineWidth = 1;
    ctx.strokeStyle = state.outBorderColor;
    //绘制整体边框
    ctx.strokeRect(
      outdager.value,
      outdager.value,
      blockSize.value * xNumber.value,
      blockSize.value * yNumber.value + baseScore.value
    );
  }
};
//主体绘制逻辑
const drawDiagram = () => {
  let ctx;
  if (canvasDom.value) {
    ctx = canvasDom.value.getContext("2d");
    //计算canvas画布所需的宽高
    canvasSize.width = outdager.value * 2 + blockSize.value * xNumber.value;
    canvasSize.height =
      outdager.value * 2 +
      baseScore.value +
      blockSize.value * yNumber.value +
      yInterval.value;
    //绘制内容部分
    nextTick(() => {
      //绘制外层边框
      drawOuterBorder();
      //绘制操作说明
      drawOperatorInfo();
    });
  }
};
onMounted(() => {
  drawDiagram();
  document.addEventListener("keydown", keyBoardOperator);
});
onBeforeMount(() => {
  document.removeEventListener("keydown", keyBoardOperator);
});
</script>
<style scoped>
canvas {
  background: white;
}
</style>

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值