基于 canvas+uniapp 的 9 宫格拼图游戏
涉及到的 canvas 基础知识
-
创建画布
<canvas id="’c1’"></canvas>
-
获取画笔
let context = uni.createCanvasContext(‘c1’)
-
绘图
/** * img原图片 * x1,y1,w1,w2 从图片指定位置开始截取指定大小的图块 * x2,y2,w2,h2 将截取的图片放置到画布指定位置,并设置大小 */ drawImage(img, x1, y1, w1, h1, x2, y2, w2, h2);
-
清除画布
/** * x,y 指定开始清除的位置 * w,h 清除区域宽高 */ clearRect(x, y, w, h);
其他知识
-
获取 canvas 画布宽高,用于计算拼图图块大小
uni .createSelectorQuery() .in(this) .select('.canvas') .boundingClientRect() .exec(res => { if (res[0]) { // res[0].width; // res[0].height; } });
-
使用二维数组定义 9 宫格图块位置
num = [ [1, 2, 3], [10, 11, 12], [20, 21, 22], ];
-
为保证拼图的随机性,需要将图块位置随机打乱,并定义标号为 22 的图块为空白图块,且始终保持在右下角,即 9 宫格游戏的第 9 个位置
-
移动滑块
- 当点击图块为空白图块相邻的图块时,交换两个图块的位置,即交换二维数组中对应的标号
- 位置更新后,重新绘制图块
-
判断游戏是否完成
- 每次交换完图块后,使用双层 for 循环,遍历 num 中数据,判断游戏是否完成
//判断游戏是否完成(每个图块位置顺序与num初始值相同即为完成拼图) checkWin() { let num = this.num; for (var i = 0; i < 3; i++) { for (var j = 0; j < 3; j++) { if (num[i][j] !== Number(i + '' + j)) { return false; } } } return true; }
整体代码
<template>
<view style="width: 100%; height: 100%">
<canvas class="canvas" canvas-id="canvasId" @touchend="touchend" id="canvasId"></canvas>
</view>
</template>
<script>
export default {
name: 'Jigsaw',
props: ['url'],
data() {
return {
context: '',
isDone: false, //是否完成拼图
canvasWidth: 0, //画布宽度
canvasHeight: 0, //画布高度
num: [
[0, 1, 2], //[00, 01, 02],
[10, 11, 12],
[20, 21, 22],
], //定义方块初始位置 22为空白图块
};
},
mounted() {
this.init();
},
methods: {
init() {
// 创建绘图对象
let context = uni.createCanvasContext('canvasId');
this.context = context;
//获取画布大小
uni
.createSelectorQuery()
.in(this)
.select('.canvas')
.boundingClientRect()
.exec(res => {
if (res[0]) {
this.canvasWidth = res[0].width;
this.canvasHeight = res[0].height;
//画布大小获取成功后再画图
this.generateNum();
this.drawCanvas();
}
});
},
//打乱图块位置
generateNum() {
for (var i = 0; i < 50; i++) {
//随机抽取其中一个数据
var i1 = Math.round(Math.random() * 2);
var j1 = Math.round(Math.random() * 2);
//再随机抽取其中一个数据
var i2 = Math.round(Math.random() * 2);
var j2 = Math.round(Math.random() * 2);
//最后一个图块位置不变
if ((i1 == 2 && j1 == 2) || (i2 == 2 && j2 == 2)) {
continue;
}
let tempNum = [].concat(this.num);
var temp = tempNum[i1][j1];
tempNum[i1][j1] = tempNum[i2][j2];
tempNum[i2][j2] = temp;
this.num = tempNum;
}
},
//根据num图块位置,画图出拼图
drawCanvas() {
//远程图片需加载至本地
uni.getImageInfo({
src: this.url,
success: img => {
//清空画布
this.context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
//画布宽高1/3 用于计算每个图块的位置及宽高
let cw = this.canvasWidth / 3;
let ch = this.canvasHeight / 3;
//图片宽高1/3 用于计算每个图块的位置及宽高
let iw = img.width / 3;
let ih = img.height / 3;
//使用双重for循环绘制3x3的拼图
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (this.num[i][j] != 22) {
//获取数值的十位数,即第几行
var row = parseInt(this.num[i][j] / 10);
//获取数组的个位数,即第几列
var col = this.num[i][j] % 10;
//在画布的相关位置上绘图
this.context.drawImage(
img.path,
col * iw,
row * ih,
iw,
ih,
j * cw,
i * ch,
cw,
ch
);
}
}
}
this.context.draw(true);
},
fail(err) {
console.log(err);
},
});
},
touchend(e) {
if (this.isDone) {
this.$u.toast('游戏已完成');
return;
}
let x = e.changedTouches[0].x;
let y = e.changedTouches[0].y;
let w = this.canvasWidth / 3;
var row = parseInt(y / w); //将x和y换算成几行几列
var col = parseInt(x / w);
if (this.num[row][col] != 22) {
//如果当前点击的不是空白区域
this.detectBox(row, col); //移动点击的方块
this.drawCanvas(); //重新绘制画布
this.isDone = this.checkWin(); //检查游戏是否成功
if (this.isDone) {
this.$emit('callback');
}
}
},
//判断游戏是否完成(每个图块位置顺序与num初始值相同即为完成拼图)
checkWin() {
let num = this.num;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (num[i][j] !== Number(i + '' + j)) {
return false;
}
}
}
return true;
},
detectBox(i, j) {
//如果点击的方块不在最上面一行
if (i > 0) {
//检测空白区域是否在当前方块的正上方
if (this.num[i - 1][j] == 22) {
//交换空白区域与当前方块的位置
this.num[i - 1][j] = this.num[i][j];
this.num[i][j] = 22;
return;
}
}
//如果点击的方块不在最下面一行
if (i < 2) {
//检测空白区域是否在当前方块的正下方
if (this.num[i + 1][j] == 22) {
//交换空白区域与当前方块的位置
this.num[i + 1][j] = this.num[i][j];
this.num[i][j] = 22;
return;
}
}
//如果点击的方块不在最左边一列
if (j > 0) {
//检测空白区域是否在当前方块的左边
if (this.num[i][j - 1] == 22) {
//交换空白区域与当前方块的位置
this.num[i][j - 1] = this.num[i][j];
this.num[i][j] = 22;
return;
}
}
//如果点击的方块不在最右边一列
if (j < 2) {
//检测空白区域是否在当前方块的右边
if (this.num[i][j + 1] == 22) {
//交换空白区域与当前方块的位置
this.num[i][j + 1] = this.num[i][j];
this.num[i][j] = 22;
return;
}
}
},
},
};
</script>
<style lang="scss" scoped>
.canvas {
height: 100%;
width: 100%;
}
</style>
原文:https://blog.csdn.net/qq_38545425/article/details/128221249