在前两节中,我们都是通过CanvasRenderingContext2D来画圆的,那么我们可不可以自己写个画圆的算法呢?
在这之前,我们先要了解怎么利用CanvasRenderingContext2D API画线?
第一步:画线
不用arc方法,自己写一个算法来画圆,在自己写画圆算法之前,首先要了解CanvasRenderingContext2D的moveTo和lineTo方法。
这里需要注意的是html dom window 对象的moveTo()方法可以把窗口的左上角移动到一个指定的坐标。而CanvasRenderingContext2D的moveTo的作用是设置当前位置并开始一条新的子路径。不能搞混淆了,一个是window.moveTo,一个是CanvasRenderingContext2D.moveTo.
语法:
moveTo(x,y)
moveTo()方法将当前位置设置为(x,y)交用它作为第一点创建一条新的子路径。除了moveTo(x,y),还需要知道:
lineTo(x,y)
lineTo()方法为当前子路径添加一条直线,这条直线从当前点开始,到(x,y)结束,最后需要用CanvasRenderingContext2D.stroke方法来连接所有路径。
通过上面这些知识,可以轻松画出一个三角形:
<canvas id="myCanvas" style="width: 200px; height: 100px; border: 1px solid #c3c3c3;">
你的浏览器不支持canvas
</canvas>
<script type="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>
运行效果如下:
第二步:另类画圆
现在回到最开始的问题:如何不通过arc方法来画一个圆形?从正三角形到正八边形,以些类推,正N边形,当N无限大时,就成为一个圆形了。根据这个原理,画出一个圆心在(150,150),半径为100的圆形。
<canvas id="myCanvas1" width="480" height="300" style=" border: 1px solid #c3c3c3;">
你的浏览器不支持canvas
</canvas>
<script type="text/javascript">
var c = document.getElementById("myCanvas1");
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>
运行效果如下:
但是,明明是在画圆,怎么没有看到画圆的过程呢?JavaScript就是这样,解释完就画完了,而不会呈现解释的过程,这也是为什么JavaScript不用考虑多线程问题,仅考虑UI线程。那么如何看到画圆的过程呢?可以使用Jscex来展示画圆的过程。
这里值得注意的是,大部分实验仅仅用到了jscex的sleep,所以可以轻松改成requestAnimationFrame、Settimeout、Setinterval的循环方式,而不是使用Jscex,复杂的线性实验或者深度嵌套使用Jscex是最佳选择。
Jscex是“Javascript Computation Expression” 的缩写,它为Javascript语言提供了一个Monadic扩展。Jscex完全使用Javascript编写,能够在支持Exmascript 3的任意引擎里使用。Jscex的JIT编译器能在运行时将Javascript代码编译成Monad形式,无需额外编译步骤,并内置异步编程类库,可以大大简化Javascript下以异步编程体验。
倘若不用Jscex,一遇到for循环,就得折算法,写回调,破坏程序的可读性,在使用Jscex来展示画圆的过程之前,先来看看Jscex经常用到的一个骨架:
var drawAsync = eval(Jscex.compile("async", function () {
//您的代码
}));
所以下面用这种格式来使用Jscex,并且通过$await(Jscex.Async.sleep(a))使线程停住a/1000秒。
<canvas id="myCanvas2" width="480" height="300" style="border: 1px solid #c3c3c3;">
你的浏览器不支持canvas
</canvas>
<script src="../Scripts/lib/uglifyjs-parser.js" type="text/javascript"></script>
<script src="../Scripts/src/jscex.js" type="text/javascript"></script>
<script src="../Scripts/src/jscex.builderBase.js" type="text/javascript"></script>
<script src="../Scripts/src/jscex.async.js" type="text/javascript"></script>
<script type="text/javascript">
var c = document.getElementById("myCanvas2");
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));
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));
var tempY = Math.pow(r * r - (x - i) * (x - i), 1 / 2);
cxt.lineTo(i, y - tempY);
cxt.stroke();
}
}));
drawAsync().start();
</script>
现在就可以看到画圆的整个过程中了。如下图: