使用canvas写一个拼图游戏

445 阅读1分钟

前言

写一个拼图游戏,效果如下:

image.png 思路:

1. 设置一个九宫格,八张图片,一个空白,创建一个随机数组定义截取图片的位置。

     随机list的values为canvas的位置,index为图片的位置;
   const imageList = new Array(8).fill(0).map((v, i) => i).sort(() => Math.random() > .5? -1: 1)
   imageList.forEach((v, i) => {
   const x = v % 3, //截取图片的列
   y = v / 3 | 0, //图片的行
   casX = i % 3, //绘制的列
   casY = i / 3 | 0 //绘制的行
   this.ctx.drawImage(image, casX * 100, casY * 100, 100, 100, x * 101, y * 101, 100, 100)})

2. 点击时做判断

  • 点击时判断位置,判断如果周围有空白时移动到指定位置,没有则认为是无效操作;
  • 重绘点击块与空白块
  • 判断if(!list.some((v, i) => v !== i)) //成功
//blankIndex 当前空白
canvas.addeventListener('click', ({offsetX, offsetY}) => {
const curIndex = (offsetX / 103 | 0) + (offsetY / 103 | 0) * 3
        if(curIndex === blankndex) return 
        if(curIndex - 3 === blankndex || curIndex + 3 === blankndex || curIndex - 1 === blankndex || curIndex + 1 === blankndex) {
           //位移并判断是否成功
           /** 
               blankndex,需要绘制的index 
               imageList.findIndex(v => v === curIndex)//需要绘制的真实x
           */
        }
})

3. 渲染canvas

drawBlock(v, i, image) {
    const x = v % 3, y = v / 3 | 0, casX = i % 3, casY = i / 3 | 0
    if(image) {
        ctx.drawImage(image, casX * 100, casY * 100, 100, 100, x * 101, y * 101, 100, 100)
    }else {
        ctx.clearRect(x * 100 + x, y * 100 + y, 100, 100)
    }  
}

drawDiff(img, blank) {
//image/black [x, y]
    this.drawBlock(img[0], img[1], image)
    this.drawBlock(blank[0], blank[1])
}

4. 最终代码(加了一个计时器+切换拼图)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>拼图游戏</title>
    <style>
        #container {
            border: 1px pink solid;
            width: 302px;
            height: 302px;
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
        }

        #jigsaw-image {
            /* visibility: hidden; */
            display: none;
        }
    </style>
</head>
<body>
    <button id="my-btn">切换图片</button>
    <div id="container">
        <canvas id="jigsaw-canvas" width="302" height="302"></canvas>
    </div>

    <script>
        const container = document.getElementById('container')
        const image = document.createElement('img')
        let myInput = document.createElement('input')
        myInput.setAttribute('type', 'file')

        class DrawCanvas {
            constructor() { 
                this.ctx = document.getElementById('jigsaw-canvas').getContext('2d')
                this.image = image
                this.blankndex = 8,
                this.imageList = []
                this.timeCount = 0
                this.timer
            }

            init() {
                this.ctx.clearRect(0, 0, 302, 302)
                this.image = image
                this.imageList = new Array(8).fill(0).map((v, i) => i)
                .sort(() => Math.random() > .5? -1: 1)
                this.draw()
            }

            draw() {
                this.imageList.forEach((v, i) => {
                    this.drawBlock(v, i, this.image)
                })
            }

            drawBlock(v, i, image) {
                const x = v % 3, y = v / 3 | 0, casX = i % 3, casY = i / 3 | 0
                if(image) {
                    this.ctx.drawImage(image, casX * 100, casY * 100, 100, 100, x * 101, y * 101, 100, 100)
                }else {
                    this.ctx.clearRect(x * 100 + x, y * 100 + y, 100, 100)
                }  
            }

            drawDiff(img, blank) {
                this.drawBlock(img[0], img[1], this.image)
                this.drawBlock(blank[0], blank[1])
            }

            click(e) {
                !this.timer && this.reckon()
                const { offsetX, offsetY } = e
                const curIndex = this.clickIndex(offsetX, offsetY)
                if(curIndex === this.blankndex) return
                if(curIndex - 3 === this.blankndex || curIndex + 3 === this.blankndex || curIndex - 1 === this.blankndex || curIndex + 1 === this.blankndex) {
                    this.drawDiff([this.blankndex, this.imageList.findIndex(v => v === curIndex)],[curIndex, this.blankndex])
                    this.imageList = this.imageList.map(v => v === curIndex? this.blankndex: v)
                    this.blankndex = curIndex
                    this.judge()
                }
            }

            judge() {
                if(!this.imageList.some((v, i) => v !== i)) {
                    alert(`成功,总共用时${this.timeCount}秒`)
                    clearInterval(this.timer)
                    this.timer = null
                    this.timeCount = 0
                    this.init()
                }
            }

            reckon() {
                this.timer = setInterval(() => {
                    this.timeCount++
                }, 1000);
            }

            clickIndex(x, y) {
                return (x / 103 | 0) + (y / 103 | 0) * 3
            }

        }
        let drawCanvas = new DrawCanvas()
        image.onload = function() {
            drawCanvas.init()
        } 

        container.addEventListener('click', (e) => {
            drawCanvas.click(e)
        })

        ;(function() {
            const localImage = localStorage.getItem('bg-image') || 'http://49.232.114.196/static/images/jigsaw-default.jpeg'
            image.setAttribute('src', localImage)
        })()

        document.getElementById('my-btn').addEventListener('click', () => {
            myInput.click()
        })

        myInput.addEventListener('change', e => {
            const file = myInput.files[0], render = new FileReader()
            render.readAsDataURL(file)
            render.onload = function(e) {
            localStorage.setItem('bg-image', render.result)
            image.setAttribute('src', render.result)
            }
        })
         

    </script>
</body>
</html>