canvas入门-五子棋

<script lang="ts" setup>
import { nextTick, onMounted, ref } from 'vue';

// contrants
const BORDER_LINES_NUM_FIFTEEN = 15;
const BORDER_PADDING_TWENTY = 20;
const BORDER_GRID_LENGTH_FORTY = 40;
const BORDER_LENGTH = 600 - BORDER_PADDING_TWENTY;

// variable
const borderRef = ref<HTMLCanvasElement>();
const context = ref<CanvasRenderingContext2D>()
const borderTop = ref(0);
const borderLeft = ref(0)
onMounted(() => {
  drawBorder()
  initChecks()
  const rect = borderRef.value.getBoundingClientRect();
  borderTop.value = rect.top;
  borderLeft.value = rect.left;
})
function getContext() {
  context.value = (borderRef.value as HTMLCanvasElement).getContext('2d') as CanvasRenderingContext2D;
}

function drawBorder() {
  getContext();

  // 画竖线
  for (let i = 0; i < BORDER_LINES_NUM_FIFTEEN; i++) {
    const positionY = BORDER_PADDING_TWENTY + i * BORDER_GRID_LENGTH_FORTY
    context.value.beginPath();
    context.value.moveTo(BORDER_PADDING_TWENTY, positionY);
    context.value.lineTo(BORDER_LENGTH, positionY);
    context.value.stroke();
    context.value.closePath();
  }

  // 画横线
  for (let index = 0; index < BORDER_LINES_NUM_FIFTEEN; index++) {
    const positionX = BORDER_PADDING_TWENTY + index * BORDER_GRID_LENGTH_FORTY;
    context.value.beginPath();
    context.value.moveTo(positionX, 20);
    context.value.lineTo(positionX, BORDER_LENGTH);
    context.value.stroke();
    context.value.closePath();
  }
}

const isBlack = ref(true);
const cheeks = ref<number[][]>([]);

function initChecks() {
  for(let i = 0; i < BORDER_LINES_NUM_FIFTEEN; i++) {
    cheeks.value[i] = new Array(BORDER_LINES_NUM_FIFTEEN).fill(0);
  }
}

async function handleClick(e: MouseEvent) {
  const { clientX, clientY } = e;

  const x = Math.round((clientX - borderLeft.value - 20) / 40) * 40 + 20
  const y = Math.round((clientY - borderTop.value - 20) / 40) * 40 + 20

  //
  const cheeksX = (x - 20) / 40;
  const cheeksY = (y - 20) / 40;

  if (cheeks.value[cheeksY][cheeksX]) {
    return
  }

  cheeks.value[cheeksY][cheeksX] = isBlack.value ? 1 : 2;
  context.value?.beginPath();
  context.value?.arc(x, y, 20, 0, 2 * Math.PI);
  context.value!.fillStyle = (isBlack.value ? 'black' : 'white');
  context.value?.fill()
  context.value?.closePath();

  if (isWin(cheeksX, cheeksY)) {
    console.log('赢了!')
    // 重新开局
    context.value?.clearRect(0, 0, 600, 600)
    drawBorder();
    initChecks();
    if (!isBlack.value) {
      isBlack.value = true
    }
  } else {
    isBlack.value = !isBlack.value;
  }
}

function isWin(x: number, y: number) {
  const flag = isBlack.value ? 1 : 2;
  // 竖排五子
  if (up_down(x, y, flag)) {
    return true;
  }
  // 横排五子
  if (left_right(x, y, flag)) {
    return true;
  }
  // /斜线五子 TODO
  // \斜线五子 TODO
}

function up_down(x: number, y: number, flag: number) {
  let num = 1;
  for (let i = 1; i < 5; i++) {
    let temY = y - i;
    if (temY < 0 || cheeks.value[temY][x] !== flag) {
      break;
    }
    if (cheeks.value[temY][x] === flag) {
      num += 1
    }
  }

  for (let i = 1; i < 5; i++) {
    let temY = y + i;
    if (temY > 14 || cheeks.value[temY][x] !== flag) {
      break;
    }
    if (cheeks.value[temY][x] === flag) {
      num += 1;
    }
  }
  return num >= 5;
}

function left_right(x: number, y: number, flag: number) {
  let num = 1;
  for (let i = 1; i < 5; i++) {
    let temX = x - i;
    if (temX < 0 || cheeks.value[y][temX] !== flag) {
      break;
    }
    if (cheeks.value[y][temX] === flag) {
      num += 1
    }
  }

  for (let i = 1; i < 5; i++) {
    let temX = x + i;
    if (y > 14 || cheeks.value[y][temX] !== flag) {
      break;
    }
    if (cheeks.value[y][temX] === flag) {
      num += 1;
    }
  }
  return num >= 5;
}
</script>

<template>
  <div class="demo">
    <!-- TODO 按钮组 -->
    <canvas ref="borderRef" id="border-canvas" width="600" height="600" @click="handleClick"></canvas>
  </div>
</template>

<style>
#border-canvas {
  background-color: #e3cdb0;
}
</style>
posted @ 2024-12-24 23:36  鱼2024  阅读(5)  评论(0)    收藏  举报