>

HTML五 canvas 小游戏练手

- 编辑:乐百家599手机首页 -

HTML五 canvas 小游戏练手

效果预览:

一、状态及其保存和恢复

在这一节开始之前,我们要先理解一下什么是状态以及状态的保存和恢复。玩过 MFC 编程的人经常能碰到这样的代码:

pOldPen=pDC->SelectObject(pNewPen)

我们在选择一个新画笔对象的同时,总是要保存住旧画笔对象,为什么要这样做呢?因为新画笔对象只是临时用一下,等用完了,我们想恢复到原来的画笔配置时,如果旧的配置事先没有被保存,这些配置就丢失了,也就没办法恢复了。

在 HTML5 绘图中,某一刻的状态就是当前这一刻上下文对象的一系列属性的配置值,只是,决定一个画笔状态的属性比较少,如颜色、粗细、线型之类的,而确定上下文状态的属性比较多,包括下面这些:

用HTML伍营造烟火效果的学科,html5烟火教程

那篇小说首要介绍了用HTML五创制烟火效果的学科,主要使用到了Canvas API,须要的对象能够参照下

要过大年了,过大年想到的正是放烟火啦。。。。于是就用canvas写了个放烟火的效益,鼠标点击也会发生烟火,不过并非爆发太多烟火哦,一个烟火散出的粒子是30到200个以内,当页面上的粒子数量达到自然的时候,页面就能够很卡咯,作者也没特意去优化神马的。以往有空再说吧。

  直接上DEMO吧:放烟火 

  原理很简短。。。就写2个烟火类以及碎屑类,实例化后让它飞起来,然后达到有些点后,把这些烟火对象的dead属性置为true,然后再实例化出肯定数额的碎屑对象,并且给予碎屑对象随机1个要到达的指标点,然后让全体碎屑对象飞过去就行了。

  【烟火】

XML/HTML Code复制内容到剪贴板

  1. var Boom = function(x,r,c,boomArea,shape){  //烟火对象   
  2.             this.booms = [];   
  3.             this.x = x;   
  4.             this.y = (canvas.height r);   
  5.             this.r = r;   
  6.             this.c = c;   
  7.             this.shape = shape || false;   
  8.             this.boomArea = boomArea;   
  9.             this.theta = 0;   
  10.             this.dead = false;   
  11.             this.ba = parseInt(getRandom(80 , 200));   
  12.         }   
  13.         Boom.prototype = {   
  14.             _paint:function(){     
  15.                 ctx.save();   
  16.                 ctx.beginPath();   
  17.                 ctx.arc(this.x,this.y,this.r,0,2*Math.PI);   
  18.                 ctx.fillStyle = this.c;   
  19.                 ctx.fill();   
  20.                 ctx.restore();   
  21.             },   
  22.             _move:function(){   
  23.                 var dx = this.boomArea.x - this.x , dy = this.boomArea.y - this.y;   
  24.                 thisthis.x = this.x dx*0.01;   
  25.                 thisthis.y = this.y dy*0.01;   
  26.   
  27.                 if(Math.abs(dx)<=this.ba && Math.abs(dy)<=this.ba){   
  28.                     if(this.shape){   
  29.                         this._shapBoom();   
  30.                     }   
  31.                     else this._boom();   
  32.                     this.dead = true;   
  33.                 }   
  34.                 else {   
  35.                     this._paint();   
  36.                 }   
  37.             },   
  38.             _drawLight:function(){   
  39.                 ctx.save();   
  40.                 ctx.fillStyle = "rgba(255,228,150,0.3)";   
  41.                 ctx.beginPath();   
  42.                 ctx.arc(this.x , this.y , this.r 3*Math.random() 1 , 0 , 2*Math.PI);   
  43.                 ctx.fill();   
  44.                 ctx.restore();   
  45.             },   
  46.             _boom:function(){    //普通爆炸   
  47.                 var fragNum = getRandom(30 , 200);   
  48.                 var style = getRandom(0,10)>=5? 1 : 2;   
  49.                 var color;   
  50.                 if(style===1){   
  51.                     color = {   
  52.                         a:parseInt(getRandom(128,255)),   
  53.                         b:parseInt(getRandom(128,255)),   
  54.                         c:parseInt(getRandom(128,255))   
  55.                     }   
  56.                 }   
  57.   
  58.                 var fanwei = parseInt(getRandom(300, 400));   
  59.                 for(var i=0;i<fragNum;i ){   
  60.                     if(style===2){   
  61.                         color = {   
  62.                             a:parseInt(getRandom(128,255)),   
  63.                             b:parseInt(getRandom(128,255)),   
  64.                             c:parseInt(getRandom(128,255))   
  65.                         }   
  66.                     }   
  67.                     var a = getRandom(-Math.PI, Math.PI);   
  68.                     var x = getRandom(0, fanwei) * Math.cos(a)   this.x;   
  69.                     var y = getRandom(0, fanwei) * Math.sin(a)   this.y;    
  70.                     var radius = getRandom(0 , 2)   
  71.                     var frag = new Frag(this.x , this.y , radius , color , x , y );   
  72.                     this.booms.push(frag);   
  73.                 }   
  74.             },   
  75.             _shapBoom:function(){    //有形状的爆炸   
  76.                 var that = this;   
  77.                 putValue(ocas , octx , this.shape , 5, function(dots){   
  78.                     var dx = canvas.width/2-that.x;   
  79.                     var dy = canvas.height/2-that.y;   
  80.                     for(var i=0;i<dots.length;i ){   
  81.                         color = {a:dots[i].a,b:dots[i].b,c:dots[i].c}   
  82.                         var x = dots[i].x;   
  83.                         var y = dots[i].y;   
  84.                         var radius = 1;   
  85.                         var frag = new Frag(that.x , that.y , radius , color , x-dx , y-dy);   
  86.                         that.booms.push(frag);   
  87.                     }   
  88.                 })   
  89.             }   
  90.         }   

  【碎屑】

XML/HTML Code复制内容到剪贴板

  1. var Frag = function(centerX , centerY , radius , color ,tx , ty){   //烟火碎屑对象   
  2.             this.tx = tx;   
  3.             this.ty = ty;   
  4.             this.x = centerX;   
  5.             this.y = centerY;   
  6.             this.dead = false;   
  7.             this.centerX = centerX;   
  8.             this.centerY = centerY;   
  9.             this.radius = radius;   
  10.             this.color = color;   
  11.         }   
  12.   
  13.         Frag.prototype = {   
  14.             paint:function(){   
  15.                 ctx.save();   
  16.                 ctx.beginPath();   
  17.                 ctx.arc(this.x , this.y , this.radius , 0 , 2*Math.PI);   
  18.                 ctx.fillStyle = "rgba(" this.color.a "," this.color.b "," this.color.c ",1)";   
  19.                 ctx.fill()   
  20.                 ctx.restore();   
  21.             },   
  22.             moveTo:function(index){   
  23.                 thisthis.ty = this.ty 0.3;   
  24.                 var dx = this.tx - this.x , dy = this.ty - this.y;   
  25.                 this.x = Math.abs(dx)<0.1 ? this.tx : (this.x dx*0.1);   
  26.                 this.y = Math.abs(dy)<0.1 ? this.ty : (this.y dy*0.1);   
  27.                 if(dx===0 && Math.abs(dy)<=80){   
  28.                     this.dead = true;   
  29.                 }   
  30.                 this.paint();   
  31.             }   
  32.         }  

  让碎屑产生虚影也很轻巧,正是历次刷新画布时,不是擦掉重绘,而是绘制反射率为0.一(假诺想虚影越来越长,能够把这一个值弄的更小)的背景颜色。然后虚影就可以做出来了。相当于:

           

XML/HTML Code复制内容到剪贴板

  1. ctx.save();   
  2.             ctx.fillStyle = "rgba(0,5,24,0.1)";   
  3.             ctx.fillRect(0,0,canvas.width,canvas.height);   
  4.             ctx.restore();  

  让烟火变成协和想要的造型,比方字体,图片之类的,也非常粗略,正是能够通过离屏canvas以及canvas的getImageData这几个法子就足以做出来。离屏canvas,以文害辞正是偏离显示器的,也便是不可知的canvas,直接在js里面用document.createElement("canvas")就足以生成一个canvas dom对象了,只要不把这些dom对象赋给body,这几个canvas对象就一定于三个离屏对象了,大家就能够收获到那几个离屏canvas的context对象,然后再用户看不到的地点做此外我们想做的作业了。

  让烟火变成和睦想要的样子便是先把文字只怕图片画在离屏canvas上,然后用getImageData获取画布上的像素数组,然后遍历数组,获取有颜色的像素,也等于大家想要的内容,保存起来后,再松手主canvas对象中显得出来。

  getImageData的像素管理我事先的博客上有讲过,若是不会用的,请戳:随意辩论用canvas来贯彻文字图片粒子化

  源码地址:

那篇小说重要介绍了用HTML5制作烟火效果的教程,主要采纳到了Canvas API,须要的朋友能够参见下 要...

DEMO演示,请猛击

 

一、当前上下文对象的位移、旋转、缩放配置

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文书档案</title>
<style type="text/css">
*{padding:0;margin:0;color:#fff;}
a {text-decoration:none;}
html,body{background-color:#000;}
#canvas{background-color:#001022;position:absolute;z-index:-1000;top:0}
#game{margin:auto;width:40%;background-color:#001022;}
#lay{width:100%;height:100%;position:relative}
#game p,h2{text-align:center;width:100%}
#gameInfo{position:relative;margin-top:150px;}

半径:

贰、当前上下文对象的 strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 属性值

#gameState{margin-top:20px;margin-left:-400px;}
#reset{position:absolute;top:20px;text-decoration:none;margin-left:150px;}
#reset:hover{color:blue}
#play{width:80px;height:40px; font-weight:bold;font-size:25px;margin:10px}
h2{font-size:40px}
#footer{margin-top:120px;display:none}
#play {background: #CCC;color: #333;border-radius: 5px;}
</style>
</head>

颜色:

三、当前上下文对象的剪裁路径配置

<body>
<div id="game">
<p><a id="reset">Reset</a></p>
<a >太空保龄球</a>
<div id="lay">
<div id="gameUI">
<div id="gameInfo">
<p><h2>Space bowling</h2></p>
<p>This is an awesome game.</p>
<p><button id="play">Play</buttom>
</div>
<div id="gameState">
<p>Asteroids:<label id="bowlNum"></label></p>
<p>   Clicks:<label id="ClickNum"></label></p>

速度:

下面那一多种布置决定了现阶段这一刻上下文对象的景观,其中移动、旋转、缩放、globalCompositeOperation(组合)、裁剪上面咱们立即会讲到。

</div>
<div id="footer">
<h2>You Win!</h2>
<p>Congratulations,you completed</p>
<p>the game in clicks</p>
<p>Play again</p>

弹性(0-1):

 

</div>
</div>
</div>
<canvas id="canvas"></canvas>
</div>
</body>
</html>
<script type="text/javascript">

入射角(0-360):

2、状态的保留与回复

var bowlNum=0;
var clickNum=0;
var viewWidth = 0;
var viewHeight = 0;
try{
viewWidth=document.documentElement.clientWidth;
viewHeight = document.documentElement.clientHeight;
}catch(e){
viewWidth = document.body.clientWidth;
viewHeight = document.body.clientHeight;
}
var canvas = document.getElementById("canvas");
//canvas.width=546
canvas.height=viewHeight;
canvas.width=viewWidth/2.5;

起始X坐标(0-400):

地点大家说某说话的情景由那么多属性决定,大家要保留这一刻的场馆就要把这几个属性值三个三个都保存,苏醒的时候在3个一个都设置回去,这也太难为了。确实是这样的,所以上下文对下提供精通三个轻巧的措施,对事态举办保存和回复,他们是:

var context = canvas.getContext("2d");
var play = document.getElementById("play");

起始Y坐标(0-400):

save( ) 和 restore( )

play.onclick=function(){//初叶游戏
document.getElementById("gameInfo").style.display="none";
document.getElementById("footer").style.display="none";
drawBegin();
}

效益表达:

save 和 restore 方法能够频繁调用,每调用二回 save 方法,调用时的动静(即一连串属性值)就压入一个栈中。

//创制小球类
function bowling(x,y,radius,mass,firction){
this.x = x;
this.y=y;
this.radius = radius;
this.mass = mass;
this.firction=firction;
this.vX=0;
this.vY=0;
this.player=true;
this.R=10;
}
var bowlingArr = new Array();

  二个基于HTML五canvas的小球物理测试系统,用户能够手动为新的小球设置分裂的属性值(颜色,半径,速度等),从而在canvas中发出小球,小球在移动进度中会收到引力,弹性以及摩擦力的熏陶。

每调用三回 restore 方法,最终三遍 save 的景色就被复苏,即出栈。

function drawBegin(){
context.fill ;
context.beginPath();
context.arc(canvas.width/2,160,80,0,2*Math.PI,true);
context.closePath();
context.fill();

乐百家前段,兑现原理:

想像一下弹匣,第3颗被发射出来的枪弹,总是最终三个被压入弹匣的。

context.fill ;
context.beginPath();
context.arc(canvas.width/2,canvas.height-60,10,0,2*Math.PI,true);
var playBowling = new bowling(canvas.width/2,canvas.height-60,0,100,10);
bowlingArr.push(playBowling);
context.closePath();
context.fill();

在小球飞行过程中,以开首速度,入射角以及重力周全作为基于,正交分解得出小球X轴和Y轴上的分速度,通过计时器不断刷新canvas,展现出小球飞行的卡通片。当小球和墙壁发生撞击时,以小球弹性为依赖总括能量消耗,当小球在墙壁滚动时,以墙壁摩擦周详为根据总计其能量消耗。

 

var radius = 0;
//画若干小球在圆台上
for(var i=0;i<8;i ){
var x=canvas.width/2;
var y=100;
radius=Math.PI/4*(i);
x = Math.floor(x Math.sin(radius)*60);
y = Math.floor(y 60-Math.cos(radius)*60);
var tempalBowling = new bowling(x,y,10,100,0.95);
tempalBowling.vX=0;
tempalBowling.vY=0;
bowlingArr.push(tempalBowling);
context.beginPath();
context.arc(x,y,10,0,Math.PI*2,true);
context.closePath();
context.fill();
}

代码分析:

三、变型

var tempalBowling = new bowling(canvas.width/2,160,10,100,0.95);
tempalBowling.vX=0;
tempalBowling.vY=0;
bowlingArr.push(tempalBowling);
context.beginPath();
context.arc(canvas.width/2,160,10,0,Math.PI*2,true);
context.closePath();
context.fill();

var bounceWall = (function() {

1、移动:translate(dx,dy)

radius = 0;
for(var i=0;i<5;i ){
var x=canvas.width/2;
var y=130;
radius=Math.PI*0.4*(i 1);
x = Math.floor(x Math.sin(radius)*30);
y = Math.floor(y 30-Math.cos(radius)*30);
var tempalBowling = new bowling(x,y,10,100,0.95);
tempalBowling.vX=0;
tempalBowling.vY=0;
bowlingArr.push(tempalBowling);
context.beginPath();
context.arc(x,y,10,0,Math.PI*2,true);
context.closePath();
context.fill();
}

returnfunction(canvasId,backgroundColor) {
this.init(canvasId, backgroundColor);

本条点子看起来很简短,其实它含有了自然的数学意义,你能够以为是全体坐标系的原点爆发了移动,新坐标系下任性一点(x,y)也就是原坐标系下的坐标为:

//画2个拖动圆台 描述允许拖动的范围

}
})();

x'=x dx
y'=y dy

}

构造函数,个中调用了prototype中的init方法开始展览开端化。须求传入的参数是canvas的ID,和canvas的背景象,假诺不传播backgroundColor参数,背景色默以为青色。

要是我们调用 ctx.translate(五,捌) 改变上下文对象的坐标系状态,然后在新意况下的点(三,二)绘图,也正是图像被绘制到了原状态下的点(捌,拾)处,即

//刷新
function UpdateUI(){
context.clearRect(0,0,canvas.width,canvas.height);

bounceWall.prototype = (function() {

x'=5 3=8
y'=5 2=10

context.fill ;
context.beginPath();
context.arc(canvas.width/2,canvas.height-60,10,0,Math.PI*2,false);
context.closePath();
context.fill();
//圆台
context.fill ;
context.beginPath();
context.arc(canvas.width/2,160,80,0,2*Math.PI,true);
context.closePath();
context.fill();
//15个小球
for(var i=0;i<bowlingArr.length;i ){
if(bowlingArr.player==false)
continue;
context.fill ;
context.beginPath();
context.arc(bowlingArr[i].x,bowlingArr[i].y,bowlingArr[i].R,0,Math.PI*2,false);
context.closePath();
context.fill();
if(bowlingArr[i].player==true)
bowlNum ;
}
//更新圆台上小球的数目
document.getElementById("bowlNum").innerHTML=bowlNum;
//更新点击释放小球的次数
document.getElementById("ClickNum").innerHTML = clickNum;
deleteBowling();

var CanvasSupport = Modernizr.canvas; //检查浏览器是还是不是援救canvas
var newBall =function(radius, color, speed, direction, currentX, currentY, elasticity) {

或是你会问,为何要那么麻烦,直接在(8,10)处绘制比行吗?譬喻把

}
//删除小球 重新设置小球
function deleteBowling(){
for(var i=1;i<bowlingArr.length;i ){
if(bowlingArr[i].vX!=0||bowlingArr[i].vY!=0){
var dx=Math.abs(bowlingArr[i].x-canvas.width/2);
var dy=Math.abs(bowlingArr[i].y-160);
var distance = Math.floor(Math.sqrt(dx*dx dy*dy));
if(distance>80){
bowlingArr[i].R-=0.5;
if(bowlingArr[i].R<=0)
bowlingArr[i].R=0;
bowlingArr[i].player=false;//不将其计入游戏了
}
}
}
//复位拖拽的小球
if(bowlingArr[0].x>canvas.width||bowlingArr[0].y<0||bowlingArr[0].y>canvas.height||bowlingArr[0].y<0){
bowlingArr[0].R=10;
bowlingArr[0].x=canvas.width/2;
bowlingArr[0].y=canvas.height-60;
bowlingArr[0].vX=0;
bowlingArr[0].vY=0;
}
}

this.radius = parseFloat(radius); //半径
this.color = color; //颜色
this.speed = parseFloat(speed); //速度
this.elasticity = parseFloat(elasticity); //弹性
this.direction = parseFloat(direction); //入射角
this.currentX = parseFloat(currentX); //初始X坐标
this.currentY = parseFloat(currentY); //初始Y坐标
this.dx = speed * Math.cos(this.direction * Math.PI /180); //总括其X轴方向的伊始速度
this.dy = speed * Math.sin(this.direction * Math.PI /180); //总计其Y轴方向的伊始速度
this.nextX =this.currentX this.dx; //依照速度和初速度得出其后一次移动到的X坐标
this.nextY =this.currentY this.dy; //依照速度和初速度得出其下一次移动到的Y坐标

ctx.translate(5,8)
ctx.drawImage(img,3,2)

//重新设置游戏
document.getElementById("reset").onclick = function(e){
var evt = e||window.event;
if(!!evt.preventDefualt){
evt.preventDefualt();
}
else{
evt.returnValue=false;
}
document.getElementById("gameInfo").style.display="block";
context.clearRect(0,0,canvas.width,canvas.height);
bowlingArr.length=0;
}

};

改成

//小球动画
function animate(){
//bowlingArr[0].y-=bowlingArr[0].vy;
for(var i=0;i<bowlingArr.length;i ){
//碰撞检查测试 方法是两两比较,借使碰撞,能量转化
var tempalA = bowlingArr[i];
for(var j=i 1;j<bowlingArr.length;j ){
var tempalB=bowlingArr[j];
var dx = Math.abs(tempalB.x-tempalA.x);
var dy = Math.abs(tempalB.y-tempalA.y);
var distanceAB = Math.sqrt(dx*dx dy*dy); //勾股定理

      起先进入到bounce wall的prototype,首先接纳Modernizr检验是或不是能够运用canvas。Modernizr是二个方可用来检查评定浏览器是不是补助部分新职能的js库,可以下载直接利用。

ctx.drawImage(img,8,10)

//检查测试碰撞
if(distanceAB<=tempalA.R tempalB.R){
//碰了
var angle =Math.atan2(dy,dx);

     之后现身的是小球的构造函数newBall,用户须求传入一名目许多的表征对其进展起首化,具体已经在疏解中标出。亟待特别注意的是里面的nextX和nextY记录的是小球下三回面世岗位的坐标,它遵照今天的职位(currentX和currentY)以及小球X轴和Y轴上的分速度(dx和dy)计算得出。nextX和nextY属性的用处首借使承接保险小球能和墙壁爆发完全的相撞,会在末端的代码分析。

那般不是更简便易行、更加直白吗?

var sine = Math.sin(angle);
var cosin = Math.cos(angle);

/* 绘制canvas的背景 */
var drawBackground =function(contextObj, backgroundColor, canvasWidth, canvasHeight) {

自家的敞亮是,移动越多的景况下是为此外图形转变服务的,得当的变动坐标原点可以让图形学总计更加好通晓,并拉动一点都不小方便,下边笔者举个简易的例子,假设:

var x=0;
var y=0;

contextObj.fillStyle = backgroundColor;
contextObj.fillRect(0, 0, parseInt(canvasWidth), parseInt(canvasHeight));

有一条线段 ,是 x 轴正迈入的一小段

var xB=dx*cosin dy*sine;
var yB=dy*cosin dx*sine;

};

y = 0 (1 <= x <= 3),

var vy=tempalA.vX*cosin tempalA.vY*sine;
var vx=tempalA.vY*cosin tempalA.vX*sine;

随后的函数是用户绘制canvas的背景,依靠的品质是用户设定的背景象,canvas的宽度和可观。

若是以坐标原点为圆心,逆时针转动90度,则线段与 y 轴正向重合,旋转后的线条为:

var vBX = tempalB.vX*cosin tempalB.vY*sine;
var vBY = tempalB.vY*cosin tempalB.vX*sine;

/* 更新小球状态 */

x = 0 (1 <= y <= 3)

var xTotal = vx-vBX;
vX = ((tempalA.mass-tempalB.mass)*vx 2*tempalB.mass*vBX)/(tempalA.mass tempalB.mass);
vBX = xTotal vx;
vY = ((tempalA.mass-tempalB.mass)*vy 2*tempalB.mass*vBY)/(tempalA.mass tempalB.mass);
vBY = xTotal vy;

var updateBallState =function(ballObj, canvasWidth, canvasHeight, gravityValue, friction) {

不过大家不或许每便旋转都是原点为圆心进行旋转,如若大家以线段的二个端点(一,0)为圆心实行旋转,我们怎么能力赢得旋转后线段上每一点的坐标值呢?其实这些进度能够分为三步:

xB = x (tempalA.radius tempalB.radius);

ballObj.currentX = ballObj.nextX;
ballObj.currentY = ballObj.nextY;
ballObj.nextX = ballObj.currentX ballObj.dx;
ballObj.nextY = ballObj.currentY ballObj.dy;

第一步:移动原点坐标到(一,0),新的线条依旧在 x 轴上,可是方程变为了:y = 0 (0 <= x <= 二)

tempalA.x = tempalA.x (x*cosin-y*sine);
tempalA.y = tempalA.y (y*cosin x*sine);

/* 测试对墙壁发生是或不是X轴碰撞 */

第三步:以新坐标系的原点为圆心进行旋转,获得新坐标系下的线条 x = 0 (0 <= y <= 贰)

tempalB.x = tempalB.x (xB*cosin-yB*sine);
tempalB.y = tempalB.y (yB*cosin yB*sine);

本文由乐百家前段发布,转载请注明来源:HTML五 canvas 小游戏练手