require.js的相关内容已在我的博文
《requireJs的使用,以canvas绘制星空为例》中描述,
可查看:
下面说一下面向对象设计canvas绘制星空的各种对象。
1、静态元素
如背景(Backgound)、土地(Land)、房屋(House)、大树(Tree),这些元素的属性如坐标(x,y)、长度(width)、高度(height)等信息是固定不变,因此我们只用向其绘制函数内传入常量参数就可以维持其状态。
2、随机元素
所谓随机,是指元素的参数信息是随机生成的,在星空绘制的canvas中,存在大量五角星,我们不可能一个个为之赋属性(太过麻烦);也不可能用定步长迭代赋值得方法赋属性(这样会使得星空失去无序性)。
所以我们采用随机+迭代的方法为星星赋值属性。
如坐标可用(Math.random()*width,+Math.random()*height/2)使星星均匀且无序填充在canvas画布上半部分。
3、动画元素
动画元素是指在canvas画布中具有动画效果的元素。在本例中包含流星和上下摆动的文本。
在动画设计中,需要不断重画canvas画布,因此需要不断调用元素的绘制函数。由于随机元素的属性实际上是随机生成固定不变的,动画元素的属性需要在原属性的基础上不断改变,所以这两种元素都需要运用面向对象的封装来保存元素状态。
元素对象模块代码如下:
element.js
define(function(){ var TextNode=function(offUnitX,offUnitY,str,direction,offLimitY){ var x,y,str; var fillStyle,font; var zx,zy,offX,offY,offUnitX,offUnitY; var speed,direction,topLimit,bottomLimit,offLimitY; //direction标识上下波动的方向 //topLimit,bottomLimit标识上下波动的界限 //默认参数 this.zx=420; this.zy=420; this.offX=22; this.offY=-5; this.speed=1; this.fillStyle="#70DBDB"; this.font="30px Georgia,Arial"; this.test=function(){ // alert(this.str.length+","+this.offY); // alert(this.str.length*this.offY/4); // alert(this.topLimit+","+this.bottomLimit); } //初始化变量 this.init=function(){ //接收参数 this.offUnitX=offUnitX; this.offUnitY=offUnitY; this.str=str; this.direction=direction; this.x=this.zx+offUnitX*this.offX; this.y=this.zy-offUnitY*this.offY; this.topLimit=this.zy+this.offY*offLimitY; this.bottomLimit=this.zy-this.offY*offLimitY; } //初始化,创建即调用 this.init(); //摆动文本 this.wave=function(){ if(this.y+this.direction*this.speedthis.bottomLimit) this.direction=-this.direction; this.y+=this.direction*this.speed; } //绘制文本 this.draw=function(cxt){ cxt.font=this.font; cxt.fillStyle=this.fillStyle; cxt.fillText(this.str,this.x,this.y); } this.setZX=function(zx){ this.zx=zx; } this.setZY=function(zy){ this.zy=zy; } this.setOffX=function(offX){ this.offX=offX; } this.setOffY=function(offY){ this.offY=offY; } this.setSpeed=function(speed){ this.speed=speed; } this.setFillStyle=function(fillStyle){ this.fillStyle=fillStyle; } this.setFont=function(font){ this.font=font; } }; var Star=function(){ var x,y,R,rotate; var fillStyle; this.fillStyle="yellow"; this.init=function(){ this.x=Math.random()*780+10; this.y=(Math.random()*580+10)*3/5; this.R=Math.random()*5+3; this.rotate=Math.random()*72; } //创建即初始化 this.init(); this.draw=function(cxt){ // 设置填充样式为黄色 cxt.fillStyle=this.fillStyle; //beginPath()新建路径 cxt.beginPath(); //五角星有10个顶点,循环设置顶点 for(var i=0;i<5;i++){ cxt.lineTo(Math.cos((72*i+this.rotate)*Math.PI/180)*this.R+this.x, Math.sin((72*i+this.rotate)*Math.PI/180)*this.R+this.y); // 在这里使用小圆半径为大圆的一半 cxt.lineTo(Math.cos((72*i+36+this.rotate)*Math.PI/180)*this.R/2+this.x, Math.sin((72*i+36+this.rotate)*Math.PI/180)*this.R/2+this.y); } //closePath()闭合路径 cxt.closePath(); //绘制 cxt.fill(); } this.setFillStyle=function(fillStyle){ this.fillStyle=fillStyle; } }; var ShootStar=function(){ var x,y,R,angle,arc,rotate; var fillStyle; var speed,delay,counter,bottom; //arc为根据角度计算出的弧度,用以计算流星的运动轨迹 //bottom为流星下落的下边界,超界则重置流星属性 //设置delay并在类中初始化为常量,标识在多少次间隔后开始本流星对象的降落 //counter为计数器,每次间隔,counter自增,当counter>=delay时,流星开始降落 //isBegin标识是否开始降落 this.bottom=Math.random()*100+500; this.delay=Math.random()*500; this.counter=0; this.fillStyle="yellow"; this.init=function(){ this.x=Math.random()*600+200; this.y=Math.random()*100+20; this.R=Math.random()*5+3; this.angle=Math.random()*30+30; this.arc=this.angle*Math.PI/180; this.rotate=Math.random()*72; this.speed=Math.random()*1+1; } this.draw=function(cxt){ cxt.fillStyle=this.fillStyle; cxt.beginPath(); for(var i=0;i<5;i++){ cxt.lineTo(Math.cos((72*i+this.angle+this.rotate)*Math.PI/180)*this.R+this.x, Math.sin((72*i+this.angle+this.rotate)*Math.PI/180)*this.R+this.y); cxt.lineTo(Math.cos((72*i+36+this.angle+this.rotate)*Math.PI/180)*this.R/2+this.x, Math.sin((72*i+36+this.angle+this.rotate)*Math.PI/180)*this.R/2+this.y); } cxt.closePath(); cxt.fill(); } //判断是否开始降落流星 this.isBegin=function(){ return ++this.counter>this.delay; } this.move=function(){ //如果流星降到canvas距离顶部bottom之内的范围,继续下降,否则流星消失 if(this.y =0){ this.x-=this.speed*Math.cos(this.arc); this.y+=this.speed*Math.sin(this.arc); this.rotate++; }else this.init(); } this.setFillStyle=function(fillStyle){ this.fillStyle=fillStyle; } }; var foo=function(){ alert(1); }; return { TextNode:TextNode, Star:Star, ShootStar:ShootStar, foo:foo };});
在本模块中为了方便外部调用,并未使用任何私有元素。
所有不是随机的变量和控制运动的变量全部设置了默认值,并添加了setter函数方便外部更改。
每种对象都包含draw(cxt)函数用于对象实例的绘制。
动画元素包含控制动画进行的函数。
程序主函数如下:
main.js
require.config({ paths:{ "jquery" :"jquery.min", "common" :"common", "element" :"element" }});require(['common','jquery'],function(common,$){ $(document).ready(function(){ var context=common.getContext("canvas",800,600); common.run(context,"SO BEAUTIFUL A NIGHT!",200,10); });});
元素绘制模块代码如下:
common.js
define(['element'],function(element){ var textSet=new Array(); var starSet=new Array(); var shootStarSet=new Array(); //获取文本对象 var getContext=function(id,width,height){ var canvas=document.getElementById(id); canvas.width=width; canvas.height=height; var context=canvas.getContext("2d"); return context; }; var initTextSet=function(str){ var lowMid=Math.floor(str.length/4); var mid=Math.floor(str.length/2); var highMid=Math.floor(str.length/4*3); //创建中点文本节点 textSet[mid]=new element.TextNode(0,0,str[mid],1,str.length/4); textSet[mid].test(); for(var i=1;i<=mid;i++){ //创建lowMid-highMid之间的节点 if(i浅绿 var grd=cxt.createLinearGradient(0,2,0,0); grd.addColorStop(0,"#040"); grd.addColorStop(1,"#5a0"); cxt.fillStyle=grd; cxt.fill(); cxt.restore(); }; //绘制一个星星 var drawStar=function(cxt){ var star=new element.Star(); star.init(); star.draw(cxt); }; //绘制starSet中的所有星星 var drawStars=function(cxt){ for(var i=0;i
最后程序入口:
index.html
Beautiful Night