实验3 绘制动画
简介
在计算机的世界中,有很多东西可以直接看得到,有很多东西已封装好了,看不到,只能用。看得到的:比如,在实验2中,设置小球的颜色和位置,小球的颜色是随机产生的,位置是计算出来的;看不到的:比如,在实验1中,调用CanvasRenderingContext2D的beginPath、arc、stroke方法就可以在画布上画一个圆,而看不到各种方法的具体算法和实现,如果不用上述方法,而是自己写一个算法去画一个圆呢?在自己写个画圆的算法之前,先要了解怎么利用CanvasRenderingContext2D API画线。
画线
不用arc方法,自己写一个算法来画圆。在自己写画圆的算法之前,首先要了解Canvas RenderingContext2D的moveTo和lineTo方法。
这里需要注意的是HTML DOM Window对象的moveTo()方法可以把窗口的左上角移动到一个指定的坐标。而CanvasRenderingContext2D的moveTo的作用是设置当前位置并开始一条新的子路径。不能搞混淆了,一个是window.moveTo,一个是CanvasRendering Context2D.moveTo。
语法
moveTo(x,y)
moveTo()方法将当前位置设置为(x,y)并用它作为第一点创建一条新的子路径。除了moveTo(x,y),还需要知道:
lineTo(x,y)
lineTo()方法为当前子路径添加一条直线。这条直线从当前点开始,到(x,y)结束。
最后需要用CanvasRenderingContext2D.stroke方法来连接所有路径。
通过上面这些知识,可以轻松画出一个三角形:
<canvasid="myCanvas"width="200"height="100"style="border:1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> <scripttype="text/javascript"> var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); cxt.moveTo(20, 10); cxt.lineTo(150, 50); cxt.lineTo(10, 50); cxt.lineTo(20, 10); cxt.stroke(); </script>
效果如图1-14所示。
图1-14 三角形
另类画圆
现在回到最开始的问题:如何不通过arc方法来画一个圆形?如图1-15所示,从正三角形到正八边形,以此类推,正N边形,当N无限大时,就成为一个圆形了。根据这个原理,画出一个圆心在(150,150),半径为100的圆形。
图1-15 正N边形
<canvasid="myCanvas"width="480"height="300"style="border:1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> <scripttype="text/javascript"> var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); var x=150; var y=150; var r=100; cxt.moveTo(x-r, y); for (var i=x-r; i < x+r+1; i++) { var tempY=Math.pow(r * r-(x-i) * (x-i), 1 / 2); cxt.lineTo(i, y+tempY); } cxt.moveTo(x-r, y); for (var i=x-r; i < x+r+1; i++) { var tempY=Math.pow(r * r-(x-i) * (x-i), 1 / 2); cxt.lineTo(i, y-tempY); } cxt.stroke(); </script>
其中用到公式:
运行效果如图1-16所示。
图1-16 圆
但是,明明是在画圆,怎么没有看到画圆的过程呢?JavaScript 就是这样,解释完就画完了,而不会呈现解释的过程,这也是为什么 JavaScript不用考虑多线程问题,仅考虑UI线程。那么如何看到画圆的过程呢?可以使用Jscex来展示画圆的过程。
注意:在以后的实验中,如果出现了Jscex.compile,默认本实验已经引用了Jscex库。
<scriptsrc="jscex.min.js"type="text/javascript"></script>
这里值得注意的是,大部分实验都仅仅用到了Jscex的sleep,所以可以轻松改成requestAnimationFrame、settimeout、setinterval的循环方式,而不使用Jscex,复杂的线性实验或者深度嵌套使用Jscex是最佳选择。
Jscex是“JavaScript Computation Expression”的缩写,它为JavaScript语言提供了一个monadic扩展。Jscex完全使用JavaScript编写,能够在支持ECMAScript 3的任意引擎里使用(如各浏览器或Node.js)。Jscex的JIT编译器能在运行时将JavaScript代码编译成Monad形式,无需额外编译步骤,并内置异步编程类库,可以大大简化JavaScript下的异步编程体验。
倘若不用Jscex,一遇到for循环,就得拆算法,写回调,破坏程序的可读性。在使用Jscex来展示画圆的过程之前,先来看看Jscex经常用到的一个骨架:
var somethingAsync=eval(Jscex.compile("async", function (a, b) { // implementation }));
所以下面用这种格式来使用Jscex,并且通过$await(Jscex.Async.sleep(a))使线程停住a/1000秒。
<canvasid="myCanvas"width="480"height="300"style="border: 1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> <scriptsrc="../Scripts/jscex.min.js"type="text/javascript"></script> <scripttype="text/javascript"> var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); var x=150; var y=150; var r=100; var drawAsync=eval(Jscex.compile("async", function () { cxt.moveTo(x-r, y); for (var i=x-r; i < x+r+1; i++) { $await(Jscex.Async.sleep(10)); \注:Sleep一会儿\ var tempY=Math.pow(r * r-(x-i) * (x-i), 1 / 2); cxt.lineTo(i, y+tempY); cxt.stroke(); } cxt.moveTo(x-r, y); for (var i=x-r; i < x+r+1; i++) { $await(Jscex.Async.sleep(10)); \注:Sleep一会儿\ var tempY=Math.pow(r * r-(x-i) * (x-i), 1 / 2); cxt.lineTo(i, y-tempY); cxt.stroke(); } })); drawAsync().start(); </script>
现在就可以看到画圆的整个过程了,如图1-17所示。
图1-17 绘制动画