最近,匆忙自学了基础的html+css+JavaScript,便想着边学边练,去写一个以前玩过的游戏——星空大战,同时也算是对于基础的一个提高。
第一阶段,我们利用canvas画出一个背景图,再把一个行星的图像画在中心处,并且能够自动旋转
项目文件夹
先列出最简单的index.html代码,主要是引入js文件和css文件,以及添加一个canvas画布标签。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>星球大战</title>
<link href="css/stytle.css" rel="stylesheet">
</head>
<body>
<canvas id="canvas"></canvas>
<script src="js/index.js">
</script>
</body>
</html>
接下来再对css文件进行描述:
/*一些网页初始化操作*/
*{margin:0;padding:0;border:0;}
body{
overflow: hidden;
height: 100%;
background: #f7f4eb;
width: 100%;
}
#canvas {
background: url('../img/space.jpg') no-repeat;
width: 100%;
height: 100%;
background-size: cover;
}
到这里,背景图就设置好了,但是当你浏览器大小发生变化时,画布里面的图像并没有填充满整个窗口。
下面就解决这个问题,我采用了事件监听的方法,一旦窗口大小发生变化这个事件发生时,就改变背景图的宽高!
//当窗口大小变化时,改变背景大小 window.addEventListener( "resize", update);
实现update函数之前,我们需要先构建canvas对象:
//Canvas
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
cH = ctx.canvas.height = window.innerHeight,
cW = ctx.canvas.width = window.innerWidth ;
下面就是update的函数实现:
function update() {
cH = ctx.canvas.height = window.innerHeight;
cW = ctx.canvas.width = window.innerWidth ;
}
通过这两步,背景图部分就完全没有问题了!
接下来就是,先填充一个圆到canvas的中心处,这样方便后面的定位操作!画图之前了解了一下canvas的坐标体系:
(0,0)在左上角
向右为x正方向
向下为y正方向
然后就是关于canvas的一些画图方法:
void ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
Canvas 2D API 绘制圆弧路径的方法。 圆弧路径的圆心在 (x, y)位置,半径为r ,根据anticlockwise (默认为顺时针)指定的方向从 startAngle开始绘制,到 endAngle
画出圆的路径后,我们需要进行填充,即用到如下方法:
beginPath()
新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。closePath()
闭合路径之后图形绘制命令又重新指向到上下文中。
stroke()
通过线条来绘制图形轮廓。
fill()
通过填充路径的内容区域生成实心的图形。
图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。使用路径绘制图形需要一些额外的步骤。
- 首先,你需要创建路径起始点。
- 然后你使用画图命令去画出路径。(在这里,画图命令就是arc()函数)
- 之后你把路径封闭。(因为是闭合的所以不用closePath())
- 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。(利用fill()来填充)
ctx.fillStyle='white';//填充的颜色设置为白色
ctx.shadowBlur=100;//模糊级数设置为100
ctx.shadowOffsetX=0;//阴影偏离x
ctx.shadowOffsetY=0;//阴影偏离y
ctx.shadowColor="#e26683";//阴影颜色
ctx.arc((cW/2),(cH/2),100,0,Math.PI*2);
ctx.fill();
画出圆的路径,设置阴影,并且填充
接下来就是画出图像了,这个也很简单有drawImage()可以直接调用:
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
具体参数含义可以参考下图:
为了方便后面的操作,我们先通过坐标系平移的方式,将(0,0)点搬移到画面中心处(cW/2,cH/2),然后通过上面的函数,将原图像切割后画到目标画布上:
ctx.translate(cW/2,cH/2);//以(cW/2,cH/2)为(0,0)
ctx.drawImage(
sprite,
0,
0,
200,
200,
-100 ,
-100 ,
200,
200);
接下来就是让这个星球给旋转起来,对此,同样有一个void ctx.rotate(angle);
它是以原点为旋转中心,然后顺时钟旋转angle的
ctx.rotate((_planet.deg+=0.1)*(Math.PI/180));//旋转的角度,_planet.deg是星球之前的角度
但是这样调用后,只是让图像静态的旋转了,如何让星球不停的旋转呢?
也就是说,我们要不停的调用这个函数,设置一个间隔时间,就会有这样的效果,但是有一种简便的方法,就是还是调用一个API,让图像能够不断重绘,当然,重绘前要清除之前的东西,然后再改变角度,画出图像就OK了
API:window.requestAnimationFrame(callback);
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
为了封装好游戏的内容,我们对浏览器加载DOM事件,加了监听,这样当页面DOM加载后,就会执行game函数,然后在执行init(),同时调用重绘的API——每次浏览器需要重绘画面的时候就会进行重新绘图。
//DOM内容加载成功
window.addEventListener('DOMContentLoaded',game);
//游戏界面的Image定义
var sprite=new Image();
sprite.src='img/sprite.png'
function game() {
console.log('DOM加载完成!');
//Canvas
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
cH = ctx.canvas.height = window.innerHeight,
cW = ctx.canvas.width = window.innerWidth ;
//Game
var gameOver=false,//游戏结束标识
_planet={deg:0};//旋转角度
//当窗口大小变化时,改变背景大小
window.addEventListener("resize", update);
function update() {
cH = ctx.canvas.height = window.innerHeight;
cW = ctx.canvas.width = window.innerWidth ;
}
//画planet
function planet() {
ctx.save();
ctx.fillStyle='white';//填充的颜色设置为白色
ctx.shadowBlur=100;//模糊级数设置为100
ctx.shadowOffsetX=0;//阴影偏离x
ctx.shadowOffsetY=0;//阴影偏离y
ctx.shadowColor="#e26683";//阴影颜色
ctx.arc((cW/2),(cH/2),100,0,Math.PI*2);
ctx.fill();
//Planet rotation
ctx.translate(cW/2,cH/2);//以(cW/2,cH/2)为(0,0)
ctx.rotate((_planet.deg+=0.1)*(Math.PI/180));//旋转的角度
ctx.drawImage(
sprite,
0,
0,
200,
200,
-100 ,
-100 ,
200,
200);
ctx.restore();//恢复之前的状态
}
//游戏开始
function start() {
if(!gameOver){//游戏没有结束
//Clear
ctx.clearRect(0,0,cW,cH);//清除画布
ctx.beginPath();
//画出Planet
planet();
}else{
console.log('游戏结束!操作未完待续...')
}
}
function init() {
window.requestAnimationFrame(init);
start();
}
init();//程序入口
}
效果: