前言
写一个拼图游戏,效果如下:
思路:
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>