我正在参加掘金社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛
我正在参加 码上掘金体验活动,详情:show出你的创意代码块
游戏地址:颜色鉴定师
开发语言:Vue3+canvas
代码地址:gitee颜色鉴定师
前言
先看一下这个gif图:
一个经典的色盲测试小游戏,相信各位前端同学看一下就有实现的思路。如果用Vue或者React来写,那简直是张飞吃豆芽,小菜一碟。这游戏实现这么简单,对大家也没什么帮助,那么怎么给实现增加一点难度嘞?答案就是用canvas绘制。「当然canvas绘制也很简单了,哈哈😂」
实现思路
看了游戏界面,如果使用现代框架,直接一个循环,配合弹性布局或者grid布局就很简单的实现了页面的布局,然后对应设置特殊的颜色,然后获取点击对应的下标。。。「不献丑了
💦」
使用canvas就需要动态获取每一个格子的坐标,然后设置随机颜色。再通过坐标对比判断是否成功点击特殊颜色。
布局实现
那么使用canvas怎么去实现布局呐?我们首先从绘制一个长方形开始: 从上面的代码片段能看出来,在绘制一个长方形的时候,需要提供起始点坐标和长宽。那么回到开始我们需要在指定的画布上绘制指定数量的长方形,所以就需要我们去动态计算每一个长方形的起始坐标以及宽高「毕竟数量是动态变化的」
/**
* @function 计算绘制数量以及坐标
* @param length 总宽度
* @returns 绘制详情
*/
const countFun = (length: number) => {
let onelength = length / nmbRow.value; // 每一个的宽度
const randColoc = randomColor(mappingFun(level.value)); // 获取颜色
const isColorX = makeRandom(nmbRow.value); // 特殊的颜色
const isColorY = makeRandom(nmbRow.value); // 特殊的颜色
const squareList = [];
for (let i = 0; i < nmbRow.value; i++) {
for (let j = 0; j < nmbRow.value; j++) {
const obj = {
x: j * onelength + 3,
y: i * onelength + 3,
width: onelength - 6,
height: onelength - 6,
color: "",
};
if (isColorX == j && isColorY == i) {
obj.color = randColoc[1];
special.value = obj; // 保存特殊颜色方块
} else {
obj.color = randColoc[0];
}
squareList.push(obj);
}
}
return squareList;
};
哈哈,比较菜所以里面使用了两层循环,分别去对应横纵对应的个数,实现添加目标熟练的数量的方块,然后再生成的同时将颜色添加进去「具体代码就不再这里展示了,感兴趣的小伙伴可以去仓库看一看」。
点击事件
我们都知道,canvas画布里面是不能单独绑定事件的,所以我们只能给整个画布绑定事件。那么怎么知道我们点击的是那个特殊的颜色?答案就是通过鼠标点击的坐标:
/**
* @function 绑定点击事件
*/
const clickCanvasFun = (e: MouseEvent) => {
const { offsetX, offsetY } = e; // 获取点击位置在画布的位置
isInSpecial(offsetX, offsetY);
};
通过获取到鼠标点击的坐标,和我们对应图形的比较来判断点击的是否是特殊颜色的方块。
/**
* @function 判断是否点击特殊颜色方块
* @param offsetX 点击横坐标
* @param offsetY 点击纵坐标
*/
const isInSpecial = (offsetX: number, offsetY: number) => {
// 成立条件:
// 1.点击纵坐标 > 特殊颜色起点的纵坐标
// 2.点击横坐标 > 特殊颜色起点的横坐标
// 3.点击纵坐标 - 特殊颜色起点的纵坐标 < 高度
// 4.点击横坐标 - 特殊颜色起点的横坐标 < 宽度
if (offsetY < special.value.y) return;
if (offsetX < special.value.x) return;
if (offsetX - special.value.x > special.value.width) return;
if (offsetY - special.value.y > special.value.height) return;
level.value = level.value + 1;
};
当然我这个是规则图形,所以只需要去判断上面的成立条件,当四个条件都成立的时候则说明当前点击在特殊颜色方块内部,那么就说明颜色鉴定成功。
那么当图形是一个不规则图形的时候,怎么判断点击点位于图形内部?这里只介绍一种交叉数法,还有比如:像素法等有兴趣的同学可以自行研究哦!
交叉数法:以某一点做射线,如果该射线与多边形的边相交的次数为奇数时,则该点在多边形内部,否则在多边形外部。
fabric.js
渲染图片
再记录一下在绘制图片的时候需要注意的地方。绘制图片使用的方法是drawImage
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
参数 | 描述 |
---|---|
img | 使用的图片 |
sx | 可选。开始剪切的 x 坐标位置。 |
sy | 可选。开始剪切的 y 坐标位置。 |
swidth | 可选。被剪切图像的宽度。 |
sheight | 可选。被剪切图像的高度。 |
x | 在画布上放置图像的 x 坐标位置。 |
y | 在画布上放置图像的 y 坐标位置。 |
width | 可选。要使用的图像的宽度。(伸展或缩小图像) |
height | 可选。要使用的图像的高度。(伸展或缩小图像) |
需要注意的问题是:图片加载需要时间,所以我们需要在图片加载成功之后再去绘制,不然就会出现代码执行不报错,页面不显示图片。具体使用方法:
/**
* 绘制初始化界面
*/
const deawBegin = () => {
context.value.clearRect(0, 0, cnWidth.value, cnWidth.value);
let bgImg = new Image();
bgImg.src = imgList[1];
context.value.beginPath();
bgImg.onload = function () {
context.value.drawImage(bgImg, 0, 0, cnWidth.value, cnWidth.value);
};
context.value.closePath(); // 结束绘制
};
setStyle();
总结
游戏的分解到这里就结束了,看起来除了麻烦点也是很简单的。总体来说虽然这些小游戏看来起很简单,但是纸上得来终觉浅,绝知此事要躬行。自己动手去实现,才会发现过程中还是有很多需要自己考虑的地方。
之前写的小游戏我汇总了一个地址,感兴趣的小伙伴可以去体验一下游戏大厅
「有帮助记得帮我点点赞哦」
最后祝各位大佬学习进步,事业有成!🎆🎆🎆
Tipes:游戏系列汇总
「小游戏系列」之拖拖拽拽的掘金拼图(PC在线游玩)🤩
「小游戏系列」之用Vue3做一个数字华容道
🔥🔥春节聚餐谁买单?我用canvas写了一个抽签小助手🎊(可在线预览)
Tipes:往期内容
「Vue系列」之Watch、WatchEffect不完全指北!
「Vue系列」之面试官问NextTick是想考察什么?
面试的时候面试官是这样问我Js基础的,角度真刁钻
「算法基础」之二叉树的遍历和搜索
「Vue系列」使用Teleport封装一个弹框组件
「Vue系列」为什么用Proxy取代Object.defineProperty?