「小游戏系列」之谁是掘金颜色鉴定师💥

1,162 阅读5分钟

我正在参加掘金社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛

我正在参加 码上掘金体验活动,详情:show出你的创意代码块

游戏地址:颜色鉴定师
开发语言:Vue3+canvas
代码地址:gitee颜色鉴定师

前言

    先看一下这个gif图:

QQ20220422-094342-HD.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?