站长资讯网
最全最丰富的资讯网站

分享用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文字效果实现实例

赞(0)
分享到: 更多 (0)
网站地图   沪ICP备18035694号-2    沪公网安备31011702889846号