实验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 绘制动画