求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
HTML5实验:JavaScript模拟流体效果
 

作者:博客园 ,发布于2012-5-24

 

把现实世界当中的物体模拟到计算机当中,一些简单的物理实验、碰撞旋转等等难度还是不算很大,难度较大的应当算流体模拟。

本文将在Canvas当中模拟出一个2D平面内的水珠,涉及的知识点和技巧包括:Jscex基础知识,贝塞尔曲线的绘制,合理利用CanvasRenderingContext2D的translate和rotate等API。

绘制椭圆

在模拟水滴之前,我们先思考一下怎么在canvas当中绘制一个椭圆。

大家可以很容易想到 下面几种方案:

1.根据椭圆笛卡尔坐标系方程绘制

2.根据椭圆极坐标方程绘制

3.根据椭圆曲率变化绘制

4.利用四条贝塞尔曲线绘制

第四中,也是性能最好的一种,这样可以避免复杂的计算,充分利用CanvasRenderingContext2D的API(API的性能是通过严格测试,一般情况下比较靠谱).

所以我们采用第四种方式来绘制椭圆。

var canvas;
   var ctx;
   canvas = document.getElementById("myCanvas1");
   ctx = canvas.getContext("2d");
   ctx.strokeStyle = "#fff";
   function drawEllipse(x, y, w, h) {
      var k = 0.5522848;
      var ox = (w / 2) * k;
      var oy = (h / 2) * k;
      var xe = x + w;
      var ye = y + h;
      var xm = x + w / 2;
      var ym = y + h / 2;
      ctx.beginPath();
      ctx.moveTo(x, ym);
      ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
      ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
      ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
      ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
      ctx.stroke();
   }
   ctx.clearRect(0,0,canvas.width,canvas.height);
   drawEllipse(10, 10, 40, 82);

(改变drawEllipse的四个参数试试)

旋转椭圆

这里的旋转不是绕上面的drawEllipse的前两个参数x,y旋转,二是绕椭圆的中心旋转。所以仅仅CanvasRenderingContext2D.rotate是不够的,因为CanvasRenderingContext2D.rotate是绕画布的左上角(0,0)旋转。所以我们先要把(0,0)通过CanvasRenderingContext2D.translate到椭圆的中心,然后再drawEllipse(-a/2, –b/2, a, b).

上面这句话,就是绕中心旋转的核心。这里还可以推广到任意图形或者图片(假设有约定的中心)。如图:

然后我们就可以先绘制一个鸟巢出来:

<html>
<head>
   <script src="http://files.cnblogs.com/iamzhanglei/jscex.jscexRequire.min.js" type="text/javascript"></script>
</head>
<body>
<style type="text/css">
   input.css3btn
   {
      background: -moz-linear-gradient(270deg, #d2ebf8, #0c8ab5);
      background: -webkit-linear-gradient(top, #d2ebf8, #0c8ab5);
      background: -o-linear-gradient(top, #d2ebf8, #0c8ab5);
      filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr='#000099CC', 
      EndColorStr='#FF0087B4');
      border-top: 1px solid #38538c;
      border-right: 1px solid #1f2d4d;
      border-bottom: 1px solid #151e33;
      border-left: 1px solid #1f2d4d;
      border-radius: 4px;
      box-shadow: inset 0 1px 10px 1px #5c8bee, 0px 1px 0 #1d2c4d, 0 2px 0px #1f3053, 0 4px 4px 1px #111111;
color: #f0f0f0;
      font: bold 20px "helvetica neue" , helvetica, arial, sans-serif;
      padding: 10px 0 10px 0;
      text-align: center;
      text-shadow: 0px -1px 1px #1e2d4d;
      width: 150px;
      background-clip: padding-box;
   }
   input.css3btn:hover
   {
      box-shadow: inset 0 0px 20px 1px #87adff, 0px 1px 0 #1d2c4d, 0 3px 0px #1f3053, 0 4px 4px 1px #111111;
cursor: pointer;
   }
   input.css3btn:active
   {box-shadow: inset 0 1px 10px 1px #5c8bee, 0 1px 0 #1d2c4d, 0 2px 0 #1f3053, 0 4px 3px 0 #111111;
margin-top: 1px;
   }
</style>
<canvas id="myCanvas2" width="350" height="350" style="border: solid 15px #222; background-color: #111;
color: #CCC;">
Your browser does not support the canvas element.
</canvas>
   <script>
      var canvas;
      var ctx;
      var px = 0;
      var py = 0;
      function init() {
         canvas = document.getElementById("myCanvas2");
         ctx = canvas.getContext("2d");
         ctx.strokeStyle = "#fff";
         ctx.translate(70, 70);
      }
      init();
      var i = 0;
      function drawEllipse(x, y, w, h) {
         var k = 0.5522848;
         var ox = (w / 2) * k;
         var oy = (h / 2) * k;
         var xe = x + w;
         var ye = y + h;
         var xm = x + w / 2;
         var ym = y + h / 2;
         ctx.beginPath();
         ctx.moveTo(x, ym);
         ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
         ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
         ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
         ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
         ctx.stroke();
         ctx.translate(x + 70, y + 100);
         px = -70;
         py = -100;
         ctx.rotate(10 * Math.PI * 2 / 360);
      }
      var ct;
      var drawAsync = eval(Jscex.compile("async", function (ct) {
         while (true) {
            drawEllipse(px, py, 140, 200)
            $await(Jscex.Async.sleep(200, ct));
         }
      }))
      function Button1_onclick() {
         ct.cancel();
      }
      function Button2_onclick() {
         ct = new Jscex.Async.CancellationToken();
         drawAsync(ct).start();
      }
   </script>
   <br />
   <input id="Button2" class="css3btn" type="button" value="run" onclick="return Button2_onclick()" />
   <input id="Button1" class="css3btn" type="button" value="stop" onclick="return Button1_onclick()" />
</body>
</html>

绘制水滴

旋转的椭圆和鸟巢神马的和水滴有什么关系呢?

我们通过改变椭圆的长轴和短轴,令其非常接近圆形(只能接近,不能等于圆形),然后每次旋转擦除画布,就可以达你预想不到的效果!

这里需要注意的是,擦除画布不再是一句CanvasRenderingContext2D.clearRect(0,0,canvas.width,canvas.height)就可以,因为画布已经旋转和画布原点已经translate,所以我们使用 ctx.clearRect(-canvas.width, -canvas.height, 2 * canvas.width, 2 * canvas.height)来擦除画布。

我们画一个长轴42,短轴40的椭圆,旋转并擦除画布:

function drawEllipse(x, y, w, h) {
  ctx.clearRect(-canvas.width, -canvas.height, 2 * canvas.width, 2 * canvas.height);
  var k = 0.5522848;
  var ox = (w / 2) * k;
  var oy = (h / 2) * k;
  var xe = x + w;
  var ye = y + h;
  var xm = x + w / 2;
  var ym = y + h / 2;
  ctx.beginPath();
  ctx.moveTo(x, ym);
  ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
  ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
  ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
  ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
  ctx.stroke();
  ctx.translate(x + 20, y + 21);
  px = -20;
  py = -21;
  ctx.rotate(10 * Math.PI * 2 / 360);
  }
  var ct;
  var drawAsync = eval(Jscex.compile("async", function (ct) {
  while (true) {
  drawEllipse(px, py, 40, 42)
  $await(Jscex.Async.sleep(10, ct));
  }
  }))

会是什么效果呢?

在线演示效果查看 http://www.cnblogs.com/iamzhanglei/archive/2011/12/12/2284188.html

相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程
 
分享到
 
 
     


十天学会DIV+CSS(WEB标准)
HTML 5的革新:结构之美
介绍27款经典的CSS框架
35个有创意的404错误页面
最容易犯的13个JavaScript错误
设计易理解和操作的网站
更多...   


设计模式原理与应用
从需求过渡到设计
软件设计原理与实践
如何编写高质量代码
单元测试、重构及持续集成
软件开发过程指南


东软集团 代码重构
某金融软件服务商 技术文档
中达电通 设计模式原理与实践
法国电信 技术文档编写与管理
西门子 嵌入式设计模式
中新大东方人寿 技术文档编写
更多...