背景
从前有个小女孩,叫多多,她特别讨厌程序员,于是,她决定用钱把程序员引诱过来,然后暴打一顿。。。
好吧,就是用 Canvas 做一个经典的接金子游戏,实现比较简单。
效果展示:
Canvas 基础
不了解 canvas 建议学习慕课网课程《炫丽的倒计时效果Canvas绘图与动画基础》和《Canvas绘图详解》 老师讲的很棒。
绘制图形
1、画多多
多多是个小女孩,但是小女孩比较难画,我决定用一颗心来代替。简单画个图,图中黑色的轮廓就会我要画的心形,指定左上角的坐标为 (x, y)
,宽度为 w*4
高度为 w*3
,而 w
是指定的一个参数。
这样就可以绘制出一个心形:
let x = 0,y = 0, w = 30;
let path = [[w, 0], [2 * w, w], [3 * w, 0], [4 * w, w], [2 * w, 3 * w]];
ctx.beginPath();
ctx.moveTo(x, y + w);
for (let i = 0; i < path.length; i++) {
ctx.lineTo(x + path[i][0], y + path[i][1]);
}
ctx.closePath();
ctx.fillStyle = 'red';
ctx.fill();
复制代码
效果如图:
2、画钱
钱的话,就简单的用矩形加文字绘制即可。
先在 (x, y)
绘制一个宽度为 w
高度为 h
的矩形。填充颜色用了渐变色。
let grd = ctx.createLinearGradient(0, 0, 0, h);
grd.addColorStop(0, '#f4cbd3');
grd.addColorStop(1, '#eb627f');
ctx.fillStyle = grd;
ctx.fillRect(x, y, w, h);
复制代码
然后绘制文字,设置中心对齐,然后在矩形中间开始绘制,保证了文字在矩形中间。
ctx.font = '15px Arial bold';
ctx.fillStyle = '#ffc24f';
ctx.textAlign= 'center';
ctx.textBaseline = 'middle';
ctx.fillText('¥100', x + w / 2, y + h / 2);
复制代码
效果如图:
3、画刀片
就是简单两个小矩形的拼接,左上角坐标为 (x, y)
,总宽度为 w
高度为 h
的矩形。
绘制代码
ctx.fillStyle = '#561d00';
ctx.fillRect(x, y, w / 3, h / 2);
ctx.fillStyle = 'silver';
ctx.fillRect(x + w / 3, y, w * 2 / 3, h);
复制代码
4、画程序员
直接用的图片。(emmm,有点丑,没找到好看的 QAQ)
在页面添加图片,并设置不显示。
<img src="./cxy.jpg" alt="" id="progImg" style="display: none">
复制代码
然后在 canvas 中绘制图片
let progImg = document.getElementById('progImg');
ctx.drawImage(progImg, x, y, w, h);
复制代码
鼠标事件的监听
对于程序员这个组件,希望能够监听鼠标事件,能够跟随鼠标移动。首先监听按下鼠标事件,此时拖动开始,监听鼠标移动事件,把程序员的坐标挪到鼠标的坐标。当鼠标抬起时,取消拖动。
canvas.onmousedown = (evt) => {
drag = true;
}
canvas.onmousemove = (evt) => {
let { offsetX, offsetY } = evt;
if (drag) {
// ... 设置程序员的位置 为鼠标的位置
}
}
document.onmouseup = () => drag = false;
复制代码
相交判断
当程序员和钱相交的时候,钱消失,当程序员和刀子相交的时候,刀子消失,所以重点是判断相交。
可以发现上面的每一个图形,我都做成了矩形,可以简化这里的相交判断。如果两个矩形的横向或纵向不重叠,证明不相交。假设第一个矩形的左上角和右下角坐标为 (x1,y1)
和 (x2,y2)
,第二个矩形的左上角和右下角坐标为 (x3,y3)
和 (x4,y4)
,判断不重叠的条件即为:
let notOverlap = x2 < x3 || x1 > x4 || y2 < y3 || y1 > y4;
复制代码
动画
要物体移动,可以使用 setInterval
每隔一段时间,就是清空页面然后重新绘制。举例:
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
let width = canvas.width;
let height = canvas.height;
let x = 0, y = 0, w = 20;
function render() {
context.clearRect(0, 0, width, height);
context.fillRect(x, y, w, w);
x++;
y++;
}
setInterval(render, 20);
复制代码
不过还可以用 window.requestAnimationFrame()
进行优化,requestAnimationFrame
有点类似 setTimeout
,不过该方法不需要设置时间间隔,而是在下一次重绘之前调用传入给该方法的动画函数。具体可以自行查阅资料。(因为其实我也不会
let render = () => {
context.clearRect(0, 0, width, height);
context.fillRect(x, y, w, w);
x++;
y++;
requestAnimationFrame(render);
};
requestAnimationFrame(render);
复制代码
以上,加起来就可以写个小游戏了。完整代码可见:http://github.com/G-lory/front-end-practice/tree/master/canvas/duoduo_beat_programmer