今天手把手教大家写 canvas 黑白块小游戏
其实 canvas 基本代码都是在 js 中写的, 我写这个是如果带入到网页中 最好用手机版查看.
- css:
- html,body{
- width: 100%;
- height: 100%;
- margin: 0;
- padding: 0;
- text-align: center;
- overflow: hidden;
- }
- html:
然后开始写 js
- // 获取宽高
- var h = document.documentElement.clientHeight;
- var w = document.documentElement.clientWidth;
- //canvas 对象
- var canvas = document.getElementById("mycan");
- // 获取绘制的环境
- var ctx = canvas.getContext("2d");
- // 设置画布宽高
- canvas.width = w;
- canvas.height = h;
这时候在浏览器中查看, 也是一片空白, 然后我们在画布中加点自己想要的东西,
比如先画三条线
- /*
- mw mh 和 mx my 是一样的 为什么要写 2 次,
- 主要是 w,h 是宽高
- xy 是坐标 所以写 2 个 做个区分
- 其实也没啥区别, 只写一个也没关系,
- 至于为什么加 1, 主要是为了让边框显示出来不至于 0,0 坐标的方块 left 和 top 没有框
- * */
- var mw = parseInt(w/4)+1;
- var mh = parseInt(h/5)+1;
- var mx = parseInt(w/4)+1;
- var my = parseInt(h/5)+1;
- function drawline(x,y){
- //beginPath 主要是为了重新绘制的时候不至于被上一个所影响或者, 不被下一个所消除
- ctx.beginPath();
- // 设置颜色
- ctx.strokeStyle = 'black';
- // 开始划线
- ctx.moveTo(x,y);
- ctx.lineTo(x,h-1);
- ctx.stroke();
- }
- function setline(){
- for(var i = 1;i <= 3; i++){
- drawline(mw*i,1);
- }
- }
- setline();
然后看看效果:
线画好了, 然后开始画框
- //new 一个数组
- var list = new Array();
- for(var i = 0; i <6; i++) {
- // 内部添加数组长度为 2 1 个是用于存储 x 值 另一个用于存储 颜色的 true 和 flase
- var arr = new Array(2);
- // 应该随机一个值 0-3
- var n = Math.floor(Math.random() * 4);
- console.log("n",n);
- arr[0] = n;
- arr[1] = true;
- // 刚开始的时候 如果 y 值 大于屏幕的高度, 就将 y 值放到最上面去至于为什么是 6
- // 因为高度是分成 5 分的 所以必须加 1 才能保证方块沾满屏幕
- if(my*i> h){
- list[i] = new kuang(my*i - my*6, arr);
- }else{
- list[i] = new kuang(my*i, arr);
- }
- }
- function drawmian(){
- /*
- 没什么用这个 canvas.width = canvas.width;
- 其实每次画布都是清空重画, 每次设置画布的宽高的时候画布自动清空, 所以先清空画布, 在从新绘制
- * */
- canvas.width = canvas.width;
- // 当然如果全是白色也太单调了, 我选择加上一张背景图片
- // 创建 img 对象
- var Img=document.createElement('img');
- // 设置 img 的路径
- Img.src='https://www.2cto.com/uploadfile/2018/0726/20180726040245324.jpg';
- // 将图片绘制到画布上去
- ctx.drawImage(Img,0,0,w,h);
- // 因为画布清空了一次, 所以需要将划线写到内部
- setline();
- for(var i = 0; i <list.length ;i++){
- ctx.beginPath();
- // 设置颜色
- if(list[i].arr[1]){
- // 绿色 不要吐槽我的审美, 我毫无审美观, 随便写的颜色
- ctx.fillStyle = "rgba(155, 187, 89, 1)";
- }else{
- // 灰色
- ctx.fillStyle = "rgba(192,192,192, 1)";
- }
- // 画方块然后填充自己设置的颜色
- ctx.fillRect(mw*list[i].arr[0] , list[i].y, mw, mh);
- ctx.strokeStyle = "white";
- // 话边框, 边框颜色自己设置
- ctx.strokeRect(mw*list[i].arr[0] , list[i].y, mw, mh);
- ctx.stroke();
- ctx.closePath();
- }
- }
- drawmian();
然后再看看效果:
解析一下 ctx.fillRect(mw*list[i].arr[0] , list[i].y, mw, mh);
这个函数就是相当于设置 一个方块参数分别的 坐标 x,y 和宽高 w,h
然后设置 x 的坐标是 mw * 先前在数组里面存的 n 值
y 坐标就是这个当前的高度
然后设置方块的大小宽高, 现在也就好理解为什么我要设置两次 mw,mh,mx 和 my 了吧
然后是在让这个 canvas 动起来
- // 这个就是设置每次移动的大小, 数字越大移动的越快
- var add = 2;
- function ksgame() {
- // 先画出来
- drawmian();
- // 然后开始循环判断
- for(var i = 0; i < list.length; i++) {
- list[i].y+=add;
- // 每当方块过了屏幕的高度
- if(list[i].y> h) {
- var n = Math.floor(Math.random() * 4);
- list[i].arr[0] = n;
- list[i].arr[1] = true;
- // 放到最上面去
- list[i].y -= mh * 6;
- }
- }
- // 这个函数相当于 setInterval , 而且比 setInterval 平滑很多,
- // 不至于看起来像卡了一样, 需要了解可以去百度一下
- time = requestAnimationFrame(goGame);
- }
- //drawmian();
- ksgame();
这个只有自己看去看效果了, 只要画布动起来了 我们只需要加上一点逻辑判断就可以, 修改一部分代码, 这个游戏就算完成了
首先在上层添加 3 个参数
- var score = 0; // 分数
- var time = null; // 用于停止动画
- var start = false; // 开始按钮
修改一下 drawmian 主要是下面添加一个分数和 开始的文本
- function drawmian() {
- /*
- 没什么用这个 canvas.width = canvas.width;
- 其实每次画布都是清空重画, 每次设置画布的宽高的时候画布自动清空, 所以先清空画布, 在从新绘制
- * */
- canvas.width = canvas.width;
- // 当然如果全是白色也太单调了, 我选择加上一张背景图片
- // 创建 img 对象
- var Img = document.createElement('img');
- // 设置 img 的路径
- Img.src = 'https://www.2cto.com/uploadfile/2018/0726/20180726040245324.jpg';
- // 将图片绘制到画布上去
- ctx.drawImage(Img, 0, 0, w, h);
- // 因为画布清空了一次, 所以需要将划线写到内部
- setline();
- var tadd = parseInt(w/2);
- for(var i = 0; i <list.length; i++) {
- ctx.beginPath();
- // 设置颜色
- if(list[i].arr[1]) {
- // 绿色 不要吐槽我的审美, 我毫无审美观, 随便写的颜色
- ctx.fillStyle = "rgba(155, 187, 89, 1)";
- } else {
- // 灰色
- ctx.fillStyle = "rgba(192,192,192, 1)";
- }
- // 画方块然后填充自己设置的颜色
- ctx.fillRect(mw * list[i].arr[0], list[i].y, mw, mh);
- ctx.strokeStyle = "white";
- // 话边框, 边框颜色自己设置
- ctx.strokeRect(mw * list[i].arr[0], list[i].y, mw, mh);
- ctx.stroke();
- ctx.closePath();
- }
- // 显示分数
- kais(tadd,50,score,"50","red");
- // 如果没有开始, 设置开始 (只是设置文字)
- if(!start){
- var x1 = (mw*list[4].arr[0] + mw/2);
- var y1 = (list[4].y + mh/2);
- kais(x1,y1,"开始");
- }
- }
然后再修改一下 ksgeme, 添加了一个 没有点击的判断
- function ksgame() {
- // 先画出来
- drawmian();
- // 然后开始循环判断
- for(var i = 0; i < list.length; i++) {
- list[i].y += add;
- // 每当方块过了屏幕的高度
- if(list[i].y> h) {
- // 添加一个判断, 如果没有点击超过了 高度, 停止动画, 并显示
- if(list[i].arr[1] == true){
- list[i].y -= 20;
- drawmian();
- window.cancelAnimationFrame(time);
- return;
- }
- var n = Math.floor(Math.random() * 4);
- list[i].arr[0] = n;
- list[i].arr[1] = true;
- // 放到最上面去
- list[i].y -= mh * 6;
- }
- }
- // 这个函数相当于 setInterval , 而且比 setInterval 平滑很多,
- // 不至于看起来像卡了一样, 需要了解可以去百度一下
- time = requestAnimationFrame(ksgame);
- }
- // 用于给添加一个文字,
- /*
- x x 坐标
- y y 坐标
- text 文本内容
- px 大小
- color 颜色
- 至于其他的位置, 我注释掉的可以自己去试下
- * */
- function kais(x,y,text,px,color){
- ctx.beginPath();
- if(px){
- ctx.font = px + "px Georgia";
- }else{
- ctx.font = "30px Georgia";
- }
- ctx.fillStyle = color color : "black";
- //ctx.textAlign="start";
- //ctx.textAlign="end";
- //ctx.textAlign="left";
- ctx.textAlign="center";
- //ctx.textAlign="right";
- // ctx.textBaseline ="top";// 顶部对齐
- // ctx.textBaseline ="hanging";// 悬挂
- // ctx.textBaseline ="middle";// 中间对齐
- // ctx.textBaseline ="bottom";// 底部对齐
- ctx.textBaseline ="alphabetic";// 默认
- ctx.fillText(text,x,y);
- ctx.stroke();
- ctx.closePath();
- }
然后添加一个点击事件, 是鼠标按下事件
- // 添加一个点击事件, 至于移动端的 触屏事件我懒得写了
- canvas.addEventListener("mousedown", function(event) {
- event = event || window.event;
- // 这是当前点击的位置
- var x = event.clientX;
- var y = event.clientY;
- if(!start){
- if(list[4].y <y && list[4].arr[0]*mw < x ){
- ksgame();
- start = !start;
- }
- }
- // 循环判断点击的是那个方块
- for(var i = 0; i < list.length; i++) {
- // 先判断点击的是那一排, 也就是高度的坐标
- if(list[i].y < y && list[i].y + mh> y) {
- // 然后再查看点击的是那一列, 也就是宽度的坐标
- var j = parseInt(x/mw);
- //j 就是点击的是哪一列
- // 然后再讲这个排的 随机数 n 于 j 对比, 如果相同, 那么点击的就是对的
- // 否则就是点的空白区
- if(list[i].arr[0] == j) {
- // 点击后将 true 变为 false 设置颜色, 而且不让多次点击得分
- if(list[i].arr[1] == true) {
- list[i].arr[1] = false;
- // 这里是分数每次超过 20, 就让速度变快一点
- if(score % 20 == 0){
- add++;
- }
- // 分数
- score++;
- }
- } else {
- console.log("进入取消");
- window.cancelAnimationFrame(time);
- drawmian();
- return;
- }
- }
- }
- })
这样一个 游戏基本没啥问题了, 如果想要完善一点, 自己写个重新开始之类的, 如果读懂了我写的代码 (应该是比较好理解了, 已经解析的这么细致了), 在继续开发应该是很容易的, 好了, 谢谢大家!, 不懂可以留言!
来源: https://www.2cto.com/kf/201807/763765.html