您好,欢迎访问一九零五行业门户网

分享用canvas实现水流和水池动画的代码

利用html 5的canvas标签绘制水流和水池动画
在利用html 5的canvas进行动画绘制之前,首先先介绍一下基本知识,包括canvas(如果对canvas比较熟悉的可以直接跳过该节)、ocanvas框架、精灵动画。在了解了以上的基本知识后,就可以开始利用canvas做动画了。
canvas标签简介在这部分,东西比较多,比较杂,各个网站上都可以找到相关的简介,在此我就不造轮子了,菜鸟上的教程就挺不错的,另外推荐一个相当好的博文玩转html 5 canvas画图,这篇文章介绍的非常详细,推荐好好看看。
canvas框架ocanvas简介canvas标签功能非常强大,既可以处理图片,又可以进行像素级的处理,完全可以取代浏览器端的flash,但是由于canvas发展还未完善,api还不是太全,元素的事件处理功能等还没有提供接口,在实现一些复杂功能时,尚需耗费许多精力,于是出现了许多第三方的基于canvas 的框架,这些框架相比于原生的canvas标签,有了更多简单易用的api,大大提高了我们编码的效率,在这里我选用的是ocanvas框架,相关的使用文档和demo都可以去上面的链接中查看。
精灵动画简介精灵动画一般由一组自定义的属性值和3个子函数组成(init、advance、draw)。
三个函数的作用分别如下:
init:初始化精灵动画的属性值
advance:再画下一帧之前首先更新下一帧的状态值
draw:将advance函数中更新的状态值绘制到画布中
以上三个函数的执行顺序是:init->advance->draw->advance->draw->…..一直循环下去。下面我以一个随机产生上升气泡的例子说明一下上面的执行过程。
var constructor_bubble = function (settings, core) { return ocanvas.extend({ core: core, shapetype: "rectangular",//下面定义了上面我们提到的三个函数:init(),advance(),draw()//在init中,我们map对象组、一个空的数组和一个代表高度的属性值 init: function () { this.map=[ {r:2,speed:3}, {r:3,speed:3}, {r:4,speed:3}, {r:5,speed:3}, {r:6,speed:3}, {r:7,speed:3}, {r:8,speed:3}, {r:9,speed:3}, {r:10,speed:3} ]; this.points=[]; this.height=this.container.height_now; },//下面是advance函数,在函数中我们利用if逻辑判断是否添加新的气泡以及进行气泡的位置更新,points数组利用队列的先进先出来存储气泡的 advance: function () { this.height=this.container.height_now; if(math.random()>0.95){ var new_point={ x:this.start.x+this.offset*2*(math.random()-0.5), y:this.start.y-this.map[0].r, r:this.map[0].r }; this.points.push(new_point); } if(this.points.length>0){ for(var i=0;i<this.points.length;i++){ this.points[i].x+=this.offset*2*(math.random()-0.5); this.points[i].y-=3; if(this.start.y-this.points[i].y>this.height-this.points[i].r-33){ this.points.shift(); } } } },//draw函数中,利用canvas的圆弧绘制指令,将points数组中存储的气泡依次画出 draw: function () { var canvas = this.core.canvas; canvas.linejoin = 'round'; canvas.linewidth = this.gdwidth; canvas.strokestyle = "#fff"; if(this.points.length>0){ for(var i=0;i<this.points.length;i++){ canvas.beginpath(); canvas.arc(this.points[i].x,this.points[i].y,5,0,2*math.pi); canvas.stroke(); canvas.closepath(); } } } }, settings); }; ocanvas.registerdisplayobject("bubble", constructor_bubble, "init");//下面是在应用中定义和添加上面定义的精灵动画,其中:start数组代表了气泡的产生点,container代表了气泡的存在区域var pp1=canvas.display.bubble({ start:{x:425,y:566}, container:sc02, width:50, offset:1, speed:5 }).add();
水流和水池动画实现下面详细介绍一下,项目中如何实现水流动画和水池动画的详细步骤:
水池动画的绘制var constructor_show = function (settings, core) { return ocanvas.extend({ core: core, shapetype: "rectangular",//上面四行都是ocanvas框架的结构语法/*下面init()、advance()、draw()分别是上节中说的动画精灵三元素,第一个用来初始化,第二个用来 更新操作,第三个用来绘制图像/动画在管道对象中,定义了一些属性,包括:x、y、height、width、start、 height_now、full、speed、fill、trail_flag、[trail]。其中x、y分别代表水池参考点相对画布左 上角的位置,height、width是水池的宽高属性,start表征了动画是否开始,height_now代表了水池中水 位的高度,full表征了水池是否填满,speed水池上涨的速度,fill水的颜色,trail_flag表征了该水池 是否是一个标准的矩形,如果不是的话,配合trail属性,指定水池的轮廓*/ init: function () { //默认动画关闭,水池full为0,当前高度为0 this.start=0; this.full=0; this.height_now=0; }, advance: function () { //如果水池未满并且是开启状态,水位未满就更新当前高度,否则将full置为1 if(this.start==1&&this.full!=1){ if (this.height_now < this.height) { this.height_now += this.speed; } else { this.full = 1; } } }, draw: function () { var canvas = this.core.canvas, //先获得水池的位置 origin = this.getorigin(), x = this.abs_x - origin.x, y = this.abs_y - origin.y; //开始绘制 canvas.beginpath(); canvas.strokestyle = "#000"; if (this.trail_flag == 1) { //如果是不规则图形,描出轮廓 canvas.moveto(this.trail[0].x_t, this.trail[0].y_t); for (var i = 1; i < this.trail.length; i++) { canvas.lineto(this.trail[i].x_t, this.trail[i].y_t); } canvas.lineto(this.trail[0].x_t, this.trail[0].y_t); canvas.clip(); } if (this.fill !== "") { //设置颜色,绘制矩形水池 canvas.fillstyle = this.fill; canvas.fillrect(x, y + this.height - this.height_now, this.width, this.height_now); } canvas.closepath(); } }, settings); };//将上面的动画精灵注册进ocanvas的display图形库中ocanvas.registerdisplayobject("sc_show", constructor_show, "init");
管道水流动画的绘制在管道水流模型中,定义了以下的属性:
destination: 当前水流最前端的位置
cells: 管道路径数组
deta: 方向斜边长
deta_x: 方向x边长
deta_y: 方向y边长
flag_x: 余弦值
flag_y:正弦值
cellindex: 当前绘制边的index
speed: 水流速度
gdwidth:水流宽度
lineheight:水流长度
x_now: 当前绘制点x坐标
y_now: 当前绘制点y坐标
firstx: 管道第一个点坐标x
firsty: 管道第一个点坐标y
beginheight: 第一段水流的起点位置
endheight: 上一段管道遗留未画完的水线
legacyheight: 最前端点在上一个管道留下的长度
paused: 是否开始
fill:水流颜色
full:是否填满
init函数//init函数主要完成初始化工作init: function () { this.x_now = this.cells[0].x_cell; this.y_now = this.cells[0].y_cell; this.firstx = this.x_now; this.firsty = this.y_now; this.endheight = 0; this.beginheight = 0; this.paused=0; this.full=0; this.cellindex = 0; this.destination.x_d = this.cells[0].x_cell; this.destination.y_d = this.cells[0].y_cell; this.legacyheight = -1; this.lineheight=10; this.speed=2*this.lineheight/20; }
advance函数//advance函数主要实现每次动画的刷新步进操作 advance: function () { if(this.paused==1){ if (this.cellindex < this.cells.length - 1) { this.deta_x = this.cells[this.cellindex + 1].x_cell - this.cells[this.cellindex].x_cell; this.deta_y = this.cells[this.cellindex + 1].y_cell - this.cells[this.cellindex].y_cell; this.deta = math.sqrt(this.deta_x * this.deta_x + this.deta_y * this.deta_y); this.flag_x = this.deta_x / this.deta; this.flag_y = this.deta_y / this.deta; if (this.legacyheight >= 0) { this.cellindex++; if (this.cellindex < this.cells.length - 1) { this.deta_x = this.cells[this.cellindex + 1].x_cell - this.cells[this.cellindex].x_cell; this.deta_y = this.cells[this.cellindex + 1].y_cell - this.cells[this.cellindex].y_cell; this.deta = math.sqrt(this.deta_x * this.deta_x + this.deta_y * this.deta_y); this.flag_x = this.deta_x / this.deta; this.flag_y = this.deta_y / this.deta; this.destination.x_d = this.cells[this.cellindex].x_cell + this.flag_x * this.legacyheight; this.destination.y_d = this.cells[this.cellindex].y_cell + this.flag_y * this.legacyheight; if (math.abs(this.destination.x_d - this.cells[this.cellindex + 1].x_cell) > this.speed * math.abs(this.flag_x) || math.abs(this.destination.y_d - this.cells[this.cellindex + 1].y_cell) > this.speed * math.abs(this.flag_y)) { this.legacyheight = -1; this.destination.x_d += this.flag_x * this.speed; this.destination.y_d += this.flag_y * this.speed; } else { if (this.flag_x == 0) { this.legacyheight = this.speed - math.abs(this.destination.y_d - this.cells[this.cellindex + 1].y_cell) / math.abs(this.flag_y); } else { this.legacyheight = this.speed - math.abs(this.destination.x_d - this.cells[this.cellindex + 1].x_cell) / math.abs(this.flag_x); } } } } else { this.destination.x_d += this.flag_x * this.speed; this.destination.y_d += this.flag_y * this.speed; if (math.abs(this.destination.x_d - this.cells[this.cellindex + 1].x_cell) >= this.speed * math.abs(this.flag_x) && math.abs(this.destination.y_d - this.cells[this.cellindex + 1].y_cell) >= this.speed * math.abs(this.flag_y)) { this.legacyheight = -1; } else { if (this.flag_x == 0) { this.legacyheight = this.speed - math.abs(this.destination.y_d - this.cells[this.cellindex + 1].y_cell) / math.abs(this.flag_y); } else { this.legacyheight = this.speed - math.abs(this.destination.x_d - this.cells[this.cellindex + 1].x_cell) / math.abs(this.flag_x); } } } }else{ this.full=1; } this.deta_x = this.cells[1].x_cell - this.cells[0].x_cell; this.deta_y = this.cells[1].y_cell - this.cells[0].y_cell; this.deta = math.sqrt(this.deta_x * this.deta_x + this.deta_y * this.deta_y); this.flag_x = this.deta_x / this.deta; this.flag_y = this.deta_y / this.deta; if (this.paused == 1) { if (math.abs(this.firstx - this.cells[0].x_cell) >= this.lineheight * math.abs(this.flag_x) && math.abs(this.firsty - this.cells[0].y_cell) >= this.lineheight * math.abs(this.flag_y)) { this.firstx = this.cells[0].x_cell; this.firsty = this.cells[0].y_cell; this.beginheight = 0; } else { if (this.beginheight < this.lineheight) { if (this.beginheight + this.speed >= this.lineheight) { this.beginheight = this.lineheight; } else { this.beginheight += this.speed; } this.firstx = this.cells[0].x_cell; this.firsty = this.cells[0].y_cell; } else if (this.beginheight == this.lineheight) { this.firstx += this.flag_x * this.speed; this.firsty += this.flag_y * this.speed; } } } } }
draw函数//draw函数在每次advance之后执行,将每次的步进更新重新绘制到画布上 draw: function () { var canvas = this.core.canvas; this.x_now = this.firstx; this.y_now = this.firsty; this.deta_x = this.cells[1].x_cell - this.cells[0].x_cell; this.deta_y = this.cells[1].y_cell - this.cells[0].y_cell; this.deta = math.sqrt(this.deta_x * this.deta_x + this.deta_y * this.deta_y); var myend = false; this.flag_x = this.deta_x / this.deta; this.flag_y = this.deta_y / this.deta; canvas.beginpath(); canvas.linejoin = 'round'; canvas.linecap="round"; this.endheight = 0; canvas.linewidth = this.gdwidth / 4; canvas.strokestyle = this.fill; if (this.beginheight > 0) { canvas.moveto(this.x_now, this.y_now); canvas.lineto(this.x_now + this.flag_x * this.beginheight, this.y_now + this.flag_y * this.beginheight); } this.x_now += this.flag_x * (this.beginheight + this.lineheight); this.y_now += this.flag_y * (this.beginheight + this.lineheight); for (var i = 1; i <= this.cellindex; i++) { myend = false; this.deta_x = this.cells[i].x_cell - this.cells[i - 1].x_cell; this.deta_y = this.cells[i].y_cell - this.cells[i - 1].y_cell; this.deta = math.sqrt(this.deta_x * this.deta_x + this.deta_y * this.deta_y); this.flag_x = this.deta_x / this.deta; this.flag_y = this.deta_y / this.deta; if (this.endheight > 0) { canvas.moveto(this.cells[i - 1].x_cell, this.cells[i - 1].y_cell); canvas.lineto(this.cells[i - 1].x_cell + this.flag_x * (this.endheight ), this.cells[i - 1].y_cell + this.flag_y * this.endheight); this.x_now = this.cells[i - 1].x_cell + this.flag_x * (this.lineheight + this.endheight); this.y_now = this.cells[i - 1].y_cell + this.flag_y * (this.lineheight + this.endheight); } if (this.endheight < 0) { this.endheight = math.abs(this.endheight); this.x_now = this.cells[i - 1].x_cell + this.flag_x * (this.endheight); this.y_now = this.cells[i - 1].y_cell + this.flag_y * (this.endheight); } if (this.endheight == 0 && i != 1) { this.x_now = this.cells[i - 1].x_cell; this.y_now = this.cells[i - 1].y_cell; } while (math.abs(this.x_now - this.cells[i].x_cell) >= this.lineheight * math.abs(this.flag_x) && math.abs(this.y_now - this.cells[i].y_cell) >= this.lineheight * math.abs(this.flag_y)) { canvas.moveto(this.x_now, this.y_now); canvas.lineto(this.x_now + this.flag_x * this.lineheight, this.y_now + this.flag_y * this.lineheight); this.x_now += this.flag_x * this.lineheight; this.y_now += this.flag_y * this.lineheight; if (math.abs(this.x_now - this.cells[i].x_cell) <= this.lineheight * math.abs(this.flag_x) && math.abs(this.y_now - this.cells[i].y_cell) <= this.lineheight * math.abs(this.flag_y)) { if (this.flag_x == 0) { this.endheight = math.abs(this.y_now - this.cells[i].y_cell) / math.abs(this.flag_y) - this.lineheight; } else { this.endheight = math.abs(this.x_now - this.cells[i].x_cell) / math.abs(this.flag_x) - this.lineheight; } //this.endheight = (math.abs(this.y_now - this.cells[i].y_cell) + math.abs(this.x_now - this.cells[i].x_cell) - this.lineheight * (math.abs(this.flag_y) + math.abs(this.flag_x)))/2; myend = true; break; } else { this.x_now += this.flag_x * this.lineheight; this.y_now += this.flag_y * this.lineheight; } } if (myend == false && math.abs(this.x_now - this.cells[i].x_cell) <= this.lineheight * math.abs(this.flag_x) && math.abs(this.y_now - this.cells[i].y_cell) <= this.lineheight * math.abs(this.flag_y)) { canvas.moveto(this.x_now, this.y_now); canvas.lineto(this.cells[i].x_cell, this.cells[i].y_cell); //this.endheight = this.lineheight - math.abs(this.x_now - this.cells[i].x_cell)*flag.x_flag - math.abs(this.y_now - this.cells[i].y_cell)*flag.y_flag; if (this.flag_x == 0) { this.endheight = this.lineheight - math.abs(this.y_now - this.cells[i].y_cell) / math.abs(this.flag_y); } else { this.endheight = this.lineheight - math.abs(this.x_now - this.cells[i].x_cell) / math.abs(this.flag_x); } //this.endheight = ( this.lineheight * (math.abs(this.flag_y) + math.abs(this.flag_x)) - math.abs(this.y_now - this.cells[i].y_cell) + math.abs(this.x_now - this.cells[i].x_cell)) / 2; } } if (this.cellindex < this.cells.length - 1) { myend = false; this.deta_x = this.cells[this.cellindex+1].x_cell-this.destination.x_d; this.deta_y = this.cells[this.cellindex+1].y_cell-this.destination.y_d; this.deta = math.sqrt(this.deta_x * this.deta_x + this.deta_y * this.deta_y); if (this.deta > 0) { this.flag_x = this.deta_x / this.deta; this.flag_y = this.deta_y / this.deta; if (this.endheight > 0) { canvas.moveto(this.cells[this.cellindex].x_cell, this.cells[this.cellindex].y_cell); canvas.lineto(this.cells[this.cellindex].x_cell + this.flag_x * (this.endheight ), this.cells[this.cellindex].y_cell + this.flag_y * this.endheight); this.x_now = this.cells[this.cellindex].x_cell + this.flag_x * ( this.endheight); this.y_now = this.cells[this.cellindex].y_cell + this.flag_y * ( this.endheight); if(math.abs(this.destination.x_d-this.x_now)>this.lineheight*math.abs(this.flag_x)||math.abs(this.destination.y_d-this.y_now)>this.lineheight*math.abs(this.flag_y)){ this.x_now+=this.lineheight*this.flag_x; this.y_now+=this.lineheight*this.flag_y; } else{ this.x_now=this.destination.x_d; this.y_now=this.destination.y_d; } if (this.endheight < 0) { this.endheight = math.abs(this.endheight); this.x_now = this.cells[this.cellindex].x_cell + this.flag_x * (this.endheight); this.y_now = this.cells[this.cellindex].y_cell + this.flag_y * (this.endheight); } if (this.endheight == 0 && this.cellindex > 0) { this.x_now = this.cells[this.cellindex].x_cell; this.y_now = this.cells[this.cellindex].y_cell; } if (this.cellindex == 0) { this.x_now = this.firstx; this.y_now = this.firsty; if (this.beginheight > 0) { canvas.moveto(this.x_now, this.y_now); canvas.lineto(this.x_now + this.flag_x * this.beginheight, this.y_now + this.flag_y * this.beginheight); } this.x_now += this.flag_x * (this.beginheight + this.lineheight); this.y_now += this.flag_y * (this.beginheight + this.lineheight); } while ((math.abs(this.x_now - this.destination.x_d) >= this.lineheight * math.abs(this.flag_x) && math.abs(this.y_now - this.destination.y_d) >this.lineheight * math.abs(this.flag_y))||(math.abs(this.x_now - this.destination.x_d) > this.lineheight * math.abs(this.flag_x) && math.abs(this.y_now - this.destination.y_d) >=this.lineheight * math.abs(this.flag_y))) { canvas.moveto(this.x_now, this.y_now); canvas.lineto(this.x_now + this.flag_x * this.lineheight, this.y_now + this.flag_y * this.lineheight); this.x_now += this.flag_x * this.lineheight; this.y_now += this.flag_y * this.lineheight; if (math.abs(this.x_now - this.destination.x_d)<= this.lineheight * math.abs(this.flag_x)&&math.abs(this.y_now - this.destination.y_d) <= this.lineheight * math.abs(this.flag_y)) { myend = true; break; } else { this.x_now += this.flag_x * this.lineheight; this.y_now += this.flag_y * this.lineheight; } } if (myend == false && math.abs(this.x_now - this.destination.x_d) < this.lineheight * math.abs(this.flag_x) || math.abs(this.y_now - this.destination.y_d) < this.lineheight * math.abs(this.flag_y)) { canvas.moveto(this.x_now, this.y_now); canvas.lineto(this.destination.x_d, this.destination.y_d); } } } canvas.stroke(); canvas.closepath(); }
取水泵房的实例下面代码在定义了一个水池对象,并赋予了相应的属性值,最后将定义的对象添加到canvas画布中。
var sc01 = canvas.display.sc_show({ x: 326, y: 200, width: 181, height: 438, height_now: 0, trail_flag: 0, t: 0, fill: color_sc, speed:speed_sc, full:0, start:0 });canvas.addchild(sc01);
下面的代码定义了一个管道对象,并且给管道对象赋予了一些初始值,最后添加到canvas画布中。
var gd01 = canvas.display.gd({ x: 0, y: 0, destination: { x_d: 0, y_d: 0 }, cells: [ {x_cell: 195, y_cell: 587}, {x_cell: 335, y_cell: 587} ], deta: 1, deta_x: 1, deta_y: 0, flag_x: 1, flag_y: 0, cellindex: 0, speed: speed_all, gdwidth: width_all, lineheight: 10, x_now: 0, y_now: 0, firstx: 0, firsty: 0, beginheight: 0, endheight: 0, legacyheight: 0, paused: 1, fill:color_gd, full:0 }); canvas.addchild(gd01);
具体动画的流程控制如下:
canvas.setloop(function () { //下面6个advance函数实现每一帧中的动画对象的更新操作 gd01.advance(); sc01.advance(); sc02.advance(); gd02.advance(); sc03.advance(); gd03.advance(); //下面几个if语句实现动画的流程控制 if(gd01.full==1){ sc01.start = 1; sc02.start = 1; } if(sc02.full==1){ gd02.paused = 1; } if(gd02.full==1) { sc03.start = 1; arrow_1.start(); arrow_2.start(); } if(sc03.full==1) { gd03.paused = 1; } canvas.redraw();//重绘画布 }).start(); //循环开始
【相关推荐】
1. 特别推荐:“php程序员工具箱”v0.1版本下载
2. h5canvas实现雪花飘落的特效代码
3. h5 canvas中fill 与stroke文字效果实现实例
以上就是分享用canvas实现水流和水池动画的代码的详细内容。
其它类似信息

推荐信息