【IT168 技术】把现实世界当中的物体模拟到计算机当中,一些简单的物理实验、碰撞旋转等等难度还是不算很大,难度较大的应当算流体模拟。
本文将在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";functiondrawEllipse(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).
上面这句话,就是绕中心旋转的核心。这里还可以推广到任意图形或者图片(假设有约定的中心)。如图:
然后我们就可以先绘制一个鸟巢出来:
Your browser doesnotsupport the canvas element.绘制水滴
旋转的椭圆和鸟巢神马的和水滴有什么关系呢?
我们通过改变椭圆的长轴和短轴,令其非常接近圆形(只能接近,不能等于圆形),然后每次旋转擦除画布,就可以达你预想不到的效果!
这里需要注意的是,擦除画布不再是一句CanvasRenderingContext2D.clearRect(0,0,canvas.width,canvas.height)就可以,因为画布已经旋转和画布原点已经translate,所以我们使用 ctx.clearRect(-canvas.width, -canvas.height, 2 * canvas.width, 2 * canvas.height)来擦除画布。
我们画一个长轴42,短轴40的椭圆,旋转并擦除画布:
functiondrawEllipse(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));
}
}))
会是什么效果呢?
现在大家可以看到一个晃动的水珠了,流体实验刚刚开始,这篇只是一个起点····