前言

大家好,我是林三心,置信大家看了我前一篇canvas入门文章为了让她10分钟入门canvas,我熬夜写了3个小我的项目和这篇文章,对canvas曾经有了入门级的理解。明天,我又用canvas写了三个乏味的小游戏,来哄你们开心,没错,我的心里只有你们,没有她。

当初是凌晨0点15分,咱们开搞,一边调试一边把这篇文章写了!!!

贪吃蛇

最终成果如下:

实现步骤分为以下几步:

  • 1、把蛇画进去
  • 2、让蛇动起来
  • 3、随机投放食物
  • 4、蛇吃食物
  • 5、边缘检测与撞本人检测

1. 把蛇画进去

其实画蛇很简略,蛇就是由蛇头和蛇身组成,而其实都能够用正方格来示意,蛇头就是一个方格,而蛇身能够是很多个方格

画方格能够用ctx.fillRect来画,蛇头应用head示意,而蛇身应用数组body来示意

// html<canvas id="canvas" width="800" height="800"></canvas>// jsdraw()function draw() {    const canvas = document.getElementById('canvas')    const ctx = canvas.getContext('2d')    // 小方格的构造函数    function Rect(x, y, width, height, color) {        this.x = x        this.y = y        this.width = width        this.height = height        this.color = color    }    Rect.prototype.draw = function () {        ctx.beginPath()        ctx.fillStyle = this.color        ctx.fillRect(this.x, this.y, this.width, this.height)        ctx.strokeRect(this.x, this.y, this.width, this.height)    }    // 蛇的构造函数    function Snake(length = 0) {        this.length = length        // 蛇头        this.head = new Rect(canvas.width / 2, canvas.height / 2, 40, 40, 'red')        // 蛇身        this.body = []        let x = this.head.x - 40        let y = this.head.y        for (let i = 0; i < this.length; i++) {            const rect = new Rect(x, y, 40, 40, 'yellow')            this.body.push(rect)            x -= 40        }    }    Snake.prototype.drawSnake = function () {        // 绘制蛇头        this.head.draw()        // 绘制蛇身        for (let i = 0; i < this.body.length; i++) {            this.body[i].draw()        }    }    const snake = new Snake(3)    snake.drawSnake()}

2. 让蛇动起来

蛇动起来有两种状况:

  • 1、蛇一开始就会默认向右挪动
  • 2、通过方向键管制,往不同方向挪动
    这两种状况每秒都是挪动一个方格的地位

让蛇动起来,其实原理很简略,我就以蛇向右挪动来举例子吧:

  • 1、蛇头先右移一个方格间隔,蛇身不动
  • 2、蛇身首部加一个方格
  • 3、蛇身尾部的方格去除
  • 4、利用定时器,造成蛇一直向右挪动的视觉

      Snake.prototype.moveSnake = function () {      // 将蛇头上一次状态,拼到蛇身首部      const rect = new Rect(this.head.x, this.head.y, this.head.width, this.head.height, 'yellow')      this.body.unshift(rect)      this.body.pop()      // 依据方向,管制蛇头的坐标      switch (this.direction) {          case 0:              this.head.x -= this.head.width              break          case 1:              this.head.y -= this.head.height              break          case 2:              this.head.x += this.head.width              break          case 3:              this.head.y += this.head.height              break      }  }  document.onkeydown = function (e) {      // 键盘事件      e = e || window.event      // 左37  上38  右39  下40      switch (e.keyCode) {          case 37:              console.log(37)              // 三元表达式,避免右挪动时按左,上面同理(贪吃蛇可不能间接掉头)              snake.direction = snake.direction === 2 ? 2 : 0              snake.moveSnake()              break          case 38:              console.log(38)              snake.direction = snake.direction === 3 ? 3 : 1              break          case 39:              console.log(39)              snake.direction = snake.direction === 0 ? 0 : 2              break          case 40:              console.log(40)              snake.direction = snake.direction === 1 ? 1 : 3              break      }  }  const snake = new Snake(3)  // 默认direction为2,也就是右  snake.direction = 2  snake.drawSnake()  function animate() {      // 先清空      ctx.clearRect(0, 0, canvas.width, canvas.height)      // 挪动      snake.moveSnake()      // 再画      snake.drawSnake()  }  var timer = setInterval(() => {      animate()  }, 100)}

    实现成果如下:

3. 随机投放食物

随机投放食物,也就是在画布中随机画一个方格,要留神以下两点:

  • 1、坐标要在画布范畴内
  • 2、食物不能投到蛇身或者蛇头上(这样会把蛇砸晕的嘿嘿)

      function randomFood(snake) {      let isInSnake = true      let rect      while (isInSnake) {          const x = Math.round(Math.random() * (canvas.width - 40) / 40) * 40          const y = Math.round(Math.random() * (canvas.height - 40) / 40) * 40          console.log(x, y)          // 保障是40的倍数啊          rect = new Rect(x, y, 40, 40, 'blue')          // 判断食物是否与蛇头蛇身重叠          if ((snake.head.x === x && snake.head.y === y) || snake.body.find(item => item.x === x && item.y === y)) {              isInSnake = true              continue          } else {              isInSnake = false          }      }      return rect  }  const snake = new Snake(3)  // 默认direction为2,也就是右  snake.direction = 2  snake.drawSnake()  // 创立随机食物实例  var food = randomFood(snake)  // 画出食物  food.draw()  function animate() {      // 先清空      ctx.clearRect(0, 0, canvas.width, canvas.height)      // 挪动      snake.moveSnake()      // 再画      snake.drawSnake()      food.draw()  }

    成果如下,随机食物画进去了:

4. 蛇吃食物

其实蛇吃食物,很简略了解,也就是蛇头挪动到跟食物的坐标重叠时,就算是吃到食物了,留神两点:

  • 1、吃到食物后,蛇身要缩短一个空格
  • 2、吃到食物后,随机食物要变换位置
const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')// 定义一个全局的是否吃到食物的一个变量let isEatFood = false        Snake.prototype.moveSnake = function () {        // 将蛇头上一次状态,拼到蛇身首部        const rect = new Rect(this.head.x, this.head.y, this.head.width, this.head.height, 'yellow')        this.body.unshift(rect)        // 判断蛇头是否与食物重叠,重叠就是吃到了,没重叠就是没吃到        isEatFood = food && this.head.x === food.x && this.head.y === food.y        // 咱们下面在蛇身首部插入方格        if (!isEatFood) {            // 没吃到就要去尾,相当于整条蛇没变长            this.body.pop()        } else {            // 吃到了就不去尾,相当于整条蛇缩短一个方格            // 并且吃到了,就要从新生成一个随机食物            food = randomFood(this)            food.draw()            isEatFood = false        }        // 依据方向,管制蛇头的坐标        switch (this.direction) {            case 0:                this.head.x -= this.head.width                break            case 1:                this.head.y -= this.head.height                break            case 2:                this.head.x += this.head.width                break            case 3:                this.head.y += this.head.height                break        }    }

5. 碰边界与碰本人

家喻户晓,蛇头碰到边界,或者碰到蛇身,都会终止游戏

    Snake.prototype.drawSnake = function () {        // 如果碰到了        if (isHit(this)) {            // 革除定时器            clearInterval(timer)            const con = confirm(`总共吃了${this.body.length - this.length}个食物,从新开始吗`)            // 是否重开            if (con) {                draw()            }            return        }        // 绘制蛇头        this.head.draw()        // 绘制蛇身        for (let i = 0; i < this.body.length; i++) {            this.body[i].draw()        }    }            function isHit(snake) {        const head = snake.head        // 是否碰到左右边界        const xLimit = head.x < 0 || head.x >= canvas.width        // 是否碰到高低边界        const yLimit = head.y < 0 || head.y >= canvas.height        // 是否撞到蛇身        const hitSelf = snake.body.find(({ x, y }) => head.x === x && head.y === y)        // 三者其中一个为true则游戏完结        return xLimit || yLimit || hitSelf    }

自此,贪吃蛇小游戏实现喽:

6. 全副代码:

draw()function draw() {    const canvas = document.getElementById('canvas')    const ctx = canvas.getContext('2d')    // 定义一个全局的是否吃到食物的一个变量    let isEatFood = false    // 小方格的构造函数    function Rect(x, y, width, height, color) {        this.x = x        this.y = y        this.width = width        this.height = height        this.color = color    }    Rect.prototype.draw = function () {        ctx.beginPath()        ctx.fillStyle = this.color        ctx.fillRect(this.x, this.y, this.width, this.height)        ctx.strokeRect(this.x, this.y, this.width, this.height)    }    // 蛇的构造函数    function Snake(length = 0) {        this.length = length        // 蛇头        this.head = new Rect(canvas.width / 2, canvas.height / 2, 40, 40, 'red')        // 蛇身        this.body = []        let x = this.head.x - 40        let y = this.head.y        for (let i = 0; i < this.length; i++) {            const rect = new Rect(x, y, 40, 40, 'yellow')            this.body.push(rect)            x -= 40        }    }    Snake.prototype.drawSnake = function () {        // 如果碰到了        if (isHit(this)) {            // 革除定时器            clearInterval(timer)            const con = confirm(`总共吃了${this.body.length - this.length}个食物,从新开始吗`)            // 是否重开            if (con) {                draw()            }            return        }        // 绘制蛇头        this.head.draw()        // 绘制蛇身        for (let i = 0; i < this.body.length; i++) {            this.body[i].draw()        }    }    Snake.prototype.moveSnake = function () {        // 将蛇头上一次状态,拼到蛇身首部        const rect = new Rect(this.head.x, this.head.y, this.head.width, this.head.height, 'yellow')        this.body.unshift(rect)        // 判断蛇头是否与食物重叠,重叠就是吃到了,没重叠就是没吃到        isEatFood = food && this.head.x === food.x && this.head.y === food.y        // 咱们下面在蛇身首部插入方格        if (!isEatFood) {            // 没吃到就要去尾,相当于整条蛇没变长            this.body.pop()        } else {            // 吃到了就不去尾,相当于整条蛇缩短一个方格            // 并且吃到了,就要从新生成一个随机食物            food = randomFood(this)            food.draw()            isEatFood = false        }        // 依据方向,管制蛇头的坐标        switch (this.direction) {            case 0:                this.head.x -= this.head.width                break            case 1:                this.head.y -= this.head.height                break            case 2:                this.head.x += this.head.width                break            case 3:                this.head.y += this.head.height                break        }    }    document.onkeydown = function (e) {        // 键盘事件        e = e || window.event        // 左37  上38  右39  下40        switch (e.keyCode) {            case 37:                console.log(37)                // 三元表达式,避免右挪动时按左,上面同理(贪吃蛇可不能间接掉头)                snake.direction = snake.direction === 2 ? 2 : 0                snake.moveSnake()                break            case 38:                console.log(38)                snake.direction = snake.direction === 3 ? 3 : 1                break            case 39:                console.log(39)                snake.direction = snake.direction === 0 ? 0 : 2                break            case 40:                console.log(40)                snake.direction = snake.direction === 1 ? 1 : 3                break        }    }    function randomFood(snake) {        let isInSnake = true        let rect        while (isInSnake) {            const x = Math.round(Math.random() * (canvas.width - 40) / 40) * 40            const y = Math.round(Math.random() * (canvas.height - 40) / 40) * 40            console.log(x, y)            // 保障是40的倍数啊            rect = new Rect(x, y, 40, 40, 'blue')            // 判断食物是否与蛇头蛇身重叠            if ((snake.head.x === x && snake.head.y === y) || snake.body.find(item => item.x === x && item.y === y)) {                isInSnake = true                continue            } else {                isInSnake = false            }        }        return rect    }    function isHit(snake) {        const head = snake.head        // 是否碰到左右边界        const xLimit = head.x < 0 || head.x >= canvas.width        // 是否碰到高低边界        const yLimit = head.y < 0 || head.y >= canvas.height        // 是否撞到蛇身        const hitSelf = snake.body.find(({ x, y }) => head.x === x && head.y === y)        // 三者其中一个为true则游戏完结        return xLimit || yLimit || hitSelf    }    const snake = new Snake(3)    // 默认direction为2,也就是右    snake.direction = 2    snake.drawSnake()    // 创立随机食物实例    var food = randomFood(snake)    // 画出食物    food.draw()    function animate() {        // 先清空        ctx.clearRect(0, 0, canvas.width, canvas.height)        // 挪动        snake.moveSnake()        // 再画        snake.drawSnake()        food.draw()    }    var timer = setInterval(() => {        animate()    }, 100)}

星星连线

成果如下,是不是很酷炫呢,兄弟们(背景图片 能够本人去下载一下):

这个小游戏可分为以下几步:

  • 1、画出单个小星星并使他挪动
  • 2、造出一百个小星星
  • 3、星星之间凑近时,进行连线
  • 4、鼠标挪动生成小星星
  • 5、鼠标点击产生5个小星星

1. 画出单个小星星,并使他挪动

其实挪动星星很简略,就是革除后从新绘制星星,并利用定时器,就会有挪动的视觉了。留神点在于:碰到边界要反弹

// html<style>    #canvas {            background: url(./光能使者.jpg) 0 0/cover no-repeat;        }</style><canvas id="canvas"></canvas>// jsconst canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')// 获取以后视图的宽度和高度let aw = document.documentElement.clientWidth || document.body.clientWidthlet ah = document.documentElement.clientHeight || document.body.clientHeight// 赋值给canvascanvas.width = awcanvas.height = ah// 屏幕变动时也要监听实时宽高window.onresize = function () {    aw = document.documentElement.clientWidth || document.body.clientWidth    ah = document.documentElement.clientHeight || document.body.clientHeight    // 赋值给canvas    canvas.width = aw    canvas.height = ah}// 本游戏无论是实心,还是线条,色调都是红色ctx.fillStyle = 'white'ctx.strokeStyle = 'white'function Star(x, y, r) {    // x,y是坐标,r是半径    this.x = x    this.y = y    this.r = r    // speed参数,在  -3 ~ 3 之间取值    this.speedX = (Math.random() * 3) * Math.pow(-1, Math.round(Math.random()))    this.speedY = (Math.random() * 3) * Math.pow(-1, Math.round(Math.random()))}Star.prototype.draw = function () {    ctx.beginPath()    ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2)    ctx.fill()    ctx.closePath()}Star.prototype.move = function () {    this.x -= this.speedX    this.y -= this.speedY    // 碰到边界时,反弹,只须要把speed取反就行    if (this.x < 0 || this.x > aw) this.speedX *= -1    if (this.y < 0 || this.y > ah) this.speedY *= -1}// 随机在canvas范畴内找一个坐标画星星const star = new Star(Math.random() * aw, Math.random() * ah, 3)star// 星星的挪动setInterval(() => {    ctx.clearRect(0, 0, aw, ah)    star.move()    star.draw()}, 50)

达到以下挪动以及反弹的成果:

2、画100个小星星

创立一个数组stars来存储这些星星

const stars = []for (let i = 0; i < 100; i++) {    // 随机在canvas范畴内找一个坐标画星星    stars.push(new Star(Math.random() * aw, Math.random() * ah, 3))}// 星星的挪动setInterval(() => {    ctx.clearRect(0, 0, aw, ah)    // 遍历挪动渲染    stars.forEach(star => {        star.move()        star.draw()    })}, 50)

成果如下:

3. 星星之间凑近时,进行连线

当两个星星的x和y相差都小于50时,就进行连线,连线只须要应用ctx.moveTo和ctx.lineTo就能够了

function drawLine(startX, startY, endX, endY) {    ctx.beginPath()    ctx.moveTo(startX, startY)    ctx.lineTo(endX, endY)    ctx.stroke()    ctx.closePath()}// 星星的挪动setInterval(() => {    ctx.clearRect(0, 0, aw, ah)    // 遍历挪动渲染    stars.forEach(star => {        star.move()        star.draw()    })    stars.forEach((star, index) => {        // 相似于冒泡排序那样,去比拟,确保所有星星两两之间都比拟到        for (let i = index + 1; i < stars.length; i++) {            if (Math.abs(star.x - stars[i].x) < 50 && Math.abs(star.y - stars[i].y) < 50) {                drawLine(star.x, star.y, stars[i].x, stars[i].y)            }        }    })}, 50)

大家能够想一想,为什么两个forEach不能何在一起去执行。这是个值得思考的问题,或者大家能够合并在一起执行,试试成果,获取就懂了。算是给大家留的一个作业哈!

成果如下:

4.鼠标挪动时带着小星星

也就是鼠标到哪,那个小星星就到哪,并且这个小星星走到哪都会跟间隔近的小星星连线

const mouseStar = new Star(0, 0, 3)canvas.onmousemove = function (e) {    mouseStar.x = e.clientX    mouseStar.y = e.clientY}// 星星的挪动setInterval(() => {    ctx.clearRect(0, 0, aw, ah)    // 鼠标星星渲染    mouseStar.draw()    // 遍历挪动渲染    stars.forEach(star => {        star.move()        star.draw()    })    stars.forEach((star, index) => {        // 相似于冒泡排序那样,去比拟,确保所有星星两两之间都比拟到        for (let i = index + 1; i < stars.length; i++) {            if (Math.abs(star.x - stars[i].x) < 50 && Math.abs(star.y - stars[i].y) < 50) {                drawLine(star.x, star.y, stars[i].x, stars[i].y)            }        }        // 判断鼠标星星连线        if (Math.abs(mouseStar.x - star.x) < 50 && Math.abs(mouseStar.y - star.y) < 50) {            drawLine(mouseStar.x, mouseStar.y, star.x, star.y)        }    })}, 50)

成果如下:

5. 鼠标点击生成五个小星星

思路就是,鼠标点击,生成5个小星星,并加到数组stars

window.onclick = function (e) {    for (let i = 0; i < 5; i++) {        stars.push(new Star(e.clientX, e.clientY, 3))    }}

成果如下:

最终成果:

6. 全副代码

const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')// 获取以后视图的宽度和高度let aw = document.documentElement.clientWidth || document.body.clientWidthlet ah = document.documentElement.clientHeight || document.body.clientHeight// 赋值给canvascanvas.width = awcanvas.height = ah// 屏幕变动时也要监听实时宽高window.onresize = function () {    aw = document.documentElement.clientWidth || document.body.clientWidth    ah = document.documentElement.clientHeight || document.body.clientHeight    // 赋值给canvas    canvas.width = aw    canvas.height = ah}// 本游戏无论是实心,还是线条,色调都是红色ctx.fillStyle = 'white'ctx.strokeStyle = 'white'function Star(x, y, r) {    // x,y是坐标,r是半径    this.x = x    this.y = y    this.r = r    // speed参数,在  -3 ~ 3 之间取值    this.speedX = (Math.random() * 3) * Math.pow(-1, Math.round(Math.random()))    this.speedY = (Math.random() * 3) * Math.pow(-1, Math.round(Math.random()))}Star.prototype.draw = function () {    ctx.beginPath()    ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2)    ctx.fill()    ctx.closePath()}Star.prototype.move = function () {    this.x -= this.speedX    this.y -= this.speedY    // 碰到边界时,反弹,只须要把speed取反就行    if (this.x < 0 || this.x > aw) this.speedX *= -1    if (this.y < 0 || this.y > ah) this.speedY *= -1}function drawLine(startX, startY, endX, endY) {    ctx.beginPath()    ctx.moveTo(startX, startY)    ctx.lineTo(endX, endY)    ctx.stroke()    ctx.closePath()}const stars = []for (let i = 0; i < 100; i++) {    // 随机在canvas范畴内找一个坐标画星星    stars.push(new Star(Math.random() * aw, Math.random() * ah, 3))}const mouseStar = new Star(0, 0, 3)canvas.onmousemove = function (e) {    mouseStar.x = e.clientX    mouseStar.y = e.clientY}window.onclick = function (e) {    for (let i = 0; i < 5; i++) {        stars.push(new Star(e.clientX, e.clientY, 3))    }}// 星星的挪动setInterval(() => {    ctx.clearRect(0, 0, aw, ah)    // 鼠标星星渲染    mouseStar.draw()    // 遍历挪动渲染    stars.forEach(star => {        star.move()        star.draw()    })    stars.forEach((star, index) => {        // 相似于冒泡排序那样,去比拟,确保所有星星两两之间都比拟到        for (let i = index + 1; i < stars.length; i++) {            if (Math.abs(star.x - stars[i].x) < 50 && Math.abs(star.y - stars[i].y) < 50) {                drawLine(star.x, star.y, stars[i].x, stars[i].y)            }        }        if (Math.abs(mouseStar.x - star.x) < 50 && Math.abs(mouseStar.y - star.y) < 50) {            drawLine(mouseStar.x, mouseStar.y, star.x, star.y)        }    })}, 50)

3. 五子棋

看看将实现的成果:


五子棋分为以下步骤:

  • 1、画出棋盘
  • 2、黑白棋切换着下,不能笼罩已下的坑位
  • 3、判断是否五连子,是的话就赢了
  • 4、彩蛋:跟AI下棋(实现单人玩游戏)

1. 画出棋盘

其实很简略,利用ctx.moveTo和ctx.lineTo,横着画15条线,竖着画15条线,就OK了。

// html#canvas {            background: #e3cdb0;        }<canvas id="canvas" width="600" height="600"></canvas>// jsplay()function play() {    const canvas = document.getElementById('canvas')    const ctx = canvas.getContext('2d')    // 绘制棋盘    // 程度,总共15条线    for (let i = 0; i < 15; i++) {        ctx.beginPath()        ctx.moveTo(20, 20 + i * 40)        ctx.lineTo(580, 20 + i * 40)        ctx.stroke()        ctx.closePath()    }    // 垂直,总共15条线    for (let i = 0; i < 15; i++) {        ctx.beginPath()        ctx.moveTo(20 + i * 40, 20)        ctx.lineTo(20 + i * 40, 580)        ctx.stroke()        ctx.closePath()    }}

这样就画出了棋盘:

2. 黑白棋切换着下

  • 1、鼠标点击事件,获取坐标,将棋画进去(ctx.arc
  • 2、确保已下的棋位不能反复下

第一步,获取鼠标坐标,然而咱们要留神一件事,棋子只能下在线的交叉处,所以拿到鼠标坐标后,要做一下解决,四舍五入,以最近的一个线交叉点为圆的圆心

第二步,如何确保棋位不反复下呢?咱们能够应用一个二维数组来记录,初始是0,下过黑棋就变为1,下过白棋就变为2,然而这里要留神一点,数组索引的x,y跟画布坐标的x,y是相同的,所以前面代码里坐标反过来,心愿大家能思考一下为啥。

// 是否下黑棋    // 黑棋先走    let isBlack = true    // 棋盘二维数组    let cheeks = []    for (let i = 0; i < 15; i++) {        cheeks[i] = new Array(15).fill(0)    }    canvas.onclick = function (e) {        const clientX = e.clientX        const clientY = e.clientY        // 对40进行取整,确保棋子落在交叉处        const x = Math.round((clientX - 20) / 40) * 40 + 20        const y = Math.round((clientY - 20) / 40) * 40 + 20        // cheeks二维数组的索引        // 这么写有点冗余,这么写你们好了解一点        const cheeksX = (x - 20) / 40        const cheeksY = (y - 20) / 40        // 对应元素不为0阐明此中央已有棋,返回        if (cheeks[cheeksY][cheeksX]) return        // 黑棋为1,白棋为2        cheeks[cheeksY][cheeksX] = isBlack ? 1 : 2        ctx.beginPath()        // 画圆        ctx.arc(x, y, 20, 0, 2 * Math.PI)        // 判断走黑还是白        ctx.fillStyle = isBlack ? 'black' : 'white'        ctx.fill()        ctx.closePath()        // 切换黑白        isBlack = !isBlack    }


成果如下:

3. 判断是否五连子

如何判断呢?有四种状况:高低五连子,左右吴连子,左上右下五连子,右上左下五连子,只有咱们每次落子的时候全副判断一次就好了。

顺便附上所有代码

play()function play() {    const canvas = document.getElementById('canvas')    const ctx = canvas.getContext('2d')    // 绘制棋盘    // 程度,总共15条线    for (let i = 0; i < 15; i++) {        ctx.beginPath()        ctx.moveTo(20, 20 + i * 40)        ctx.lineTo(580, 20 + i * 40)        ctx.stroke()        ctx.closePath()    }    // 垂直,总共15条线    for (let i = 0; i < 15; i++) {        ctx.beginPath()        ctx.moveTo(20 + i * 40, 20)        ctx.lineTo(20 + i * 40, 580)        ctx.stroke()        ctx.closePath()    }    // 是否下黑棋    // 黑棋先走    let isBlack = true    // 棋盘二维数组    let cheeks = []    for (let i = 0; i < 15; i++) {        cheeks[i] = new Array(15).fill(0)    }    canvas.onclick = function (e) {        const clientX = e.clientX        const clientY = e.clientY        // 对40进行取整,确保棋子落在交叉处        const x = Math.round((clientX - 20) / 40) * 40 + 20        const y = Math.round((clientY - 20) / 40) * 40 + 20        // cheeks二维数组的索引        // 这么写有点冗余,这么写你们好了解一点        const cheeksX = (x - 20) / 40        const cheeksY = (y - 20) / 40        // 对应元素不为0阐明此中央已有棋,返回        if (cheeks[cheeksY][cheeksX]) return        // 黑棋为1,白棋为2        cheeks[cheeksY][cheeksX] = isBlack ? 1 : 2        ctx.beginPath()        // 画圆        ctx.arc(x, y, 20, 0, 2 * Math.PI)        // 判断走黑还是白        ctx.fillStyle = isBlack ? 'black' : 'white'        ctx.fill()        ctx.closePath()        // canvas画图是异步的,保障画进去再去检测输赢        setTimeout(() => {            if (isWin(cheeksX, cheeksY)) {                const con = confirm(`${isBlack ? '黑棋' : '白棋'}赢了!是否从新开局?`)                // 从新开局                ctx.clearRect(0, 0, 600, 600)                con && play()            }            // 切换黑白            isBlack = !isBlack        }, 0)    }    // 判断是否五连子    function isWin(x, y) {        const flag = isBlack ? 1 : 2        // 上和下        if (up_down(x, y, flag)) {            return true        }        // 左和右        if (left_right(x, y, flag)) {            return true        }        // 左上和右下        if (lu_rd(x, y, flag)) {            return true        }        // 右上和左下        if (ru_ld(x, y, flag)) {            return true        }        return false    }    function up_down(x, y, flag) {        let num = 1        // 向上找        for (let i = 1; i < 5; i++) {            let tempY = y - i            console.log(x, tempY)            if (tempY < 0 || cheeks[tempY][x] !== flag) break            if (cheeks[tempY][x] === flag) num += 1        }        // 向下找        for (let i = 1; i < 5; i++) {            let tempY = y + i            console.log(x, tempY)            if (tempY > 14 || cheeks[tempY][x] !== flag) break            if (cheeks[tempY][x] === flag) num += 1        }        return num >= 5    }    function left_right(x, y, flag) {        let num = 1        // 向左找        for (let i = 1; i < 5; i++) {            let tempX = x - i            if (tempX < 0 || cheeks[y][tempX] !== flag) break            if (cheeks[y][tempX] === flag) num += 1        }        // 向右找        for (let i = 1; i < 5; i++) {            let tempX = x + i            if (tempX > 14 || cheeks[y][tempX] !== flag) break            if (cheeks[y][tempX] === flag) num += 1        }        return num >= 5    }    function lu_rd(x, y, flag) {        let num = 1        // 向左上找        for (let i = 1; i < 5; i++) {            let tempX = x - i            let tempY = y - i            if (tempX < 0 || tempY < 0 || cheeks[tempY][tempX] !== flag) break            if (cheeks[tempY][tempX] === flag) num += 1        }        // 向右下找        for (let i = 1; i < 5; i++) {            let tempX = x + i            let tempY = y + i            if (tempX > 14 || tempY > 14 || cheeks[tempY][tempX] !== flag) break            if (cheeks[tempY][tempX] === flag) num += 1        }        return num >= 5    }    function ru_ld(x, y, flag) {        let num = 1        // 向右上找        for (let i = 1; i < 5; i++) {            let tempX = x - i            let tempY = y + i            if (tempX < 0 || tempY > 14 || cheeks[tempY][tempX] !== flag) break            if (cheeks[tempY][tempX] === flag) num += 1        }        // 向左下找        for (let i = 1; i < 5; i++) {            let tempX = x + i            let tempY = y - i            if (tempX > 14 || tempY < 0 || cheeks[tempY][tempX] !== flag) break            if (cheeks[tempY][tempX] === flag) num += 1        }        return num >= 5    }}

4. 彩蛋:与AI下棋

其实很简略,每次下完棋,设置一个函数:随机找地位下棋。这样就实现了和电脑下棋,单人游戏的性能了,这个性能我曾经实现,然而我就不写进去了,交给大家吧,当做是大家坚固这篇文章的作业。哈哈哈哈

结语

我是林三心,一个热心的前端菜鸟程序员。如果你上进,喜爱前端,想学习前端,那咱们能够交朋友,一起摸鱼哈哈,摸鱼群,加我请备注【思否】