基于HTML5的动态线
1 起因
某天晚上在微信公众号中看到一篇令人上瘾的科学动图,我对这类实验十分感兴趣,但自己并不是科学工作者,所以也就仅限于感兴趣为止。
不过其中有一张地球和金星的运动轨迹,如下图:
我觉得这个我可以实现,于是就抽出时间自己做了个类似的小东西。
2 结构和思路
动图的结构是内外两个同心圆,两个动点分别在两个同心圆上运动,间隔一定的时间将两点连线,最后就能组成想要的图案了。
正因为思路和结构十分简单,我才觉得自己能够实现出来。
事实上确实很容易实现。
3 Canvas
HTML5的<canvas>
标签提供了绝好的画板,比自己实现BMP图像容易多了,而且可以通过JavaScript来做出动画效果。
我设置的画布是511×511的,这是为了让圆心在正中间,没有特殊的含义。
首先初始化成黑色。然后开始画图。
4 画线
在Canvas中改变某一点的颜色很容易。使用Canvas的经验得自于Milo Yip的用JavaScript玩转计算机图形学。
Color的代码也是借鉴他的实现方式,我也考虑了直接使用数组,但还是选择了使用更清晰的Color。
之后就是其他的组成元素,Point、Line和Circle。
画线是最难的。我自己写了一个画线的方法,用的就是最简单直观的算法。
根据起点和终点的坐标,和每条直线都是形如 y=ax+b 的函数这一点,根据每个点的横坐标计算纵坐标。
由于并非每个 x 所对应的
Math.round()
,让直线看起来更平滑。
但这个做法并不完全起效。
我通过随机生成终点和起点坐标的方式来测试这种算法的效果,发现有些直线很合理,但也有一些不那么合理,直线变成了稀疏的点阵。
于是我搜索了一下一般的图形引擎中是如何实现画线算法的,得到了Bresenham算法。
在复刻这个算法之前,我也明白了为什么我的算法并不一直有效。
我曾经也写过画线的算法,遇到了和现在一样的问题。尽管当时没有解决,但是意识到了问题所在:由于像素本身是稀疏的点阵(没有小数坐标),所以每个 x 所对应的
用更数学,或者更计算机科学一点的语言来说,就是当直线斜率的绝对值大于1时,一些x对应多个y值。
这时候需要将直线沿着 y=x (如果斜率小于-1,则是 y=−x )翻转一下,使得斜率重新纳入[-1, 1]的范围。
5 Bresenham算法
Bresenham算法是一组常用的图形算法之一,上边这个算法太过直观,所以实现起来虽然简单,但性能上有所欠缺。
Bresenham算法则使用另一种计算方式。
但画直线这种事情,无论怎么算,都是基于数学的,而数学上的一条直线就是一个一元一次函数。这一点没有变。
Bresenham算法更多的是将计算过程从浮点运算和除法运算变成了整数的加法。这一点对计算机的性能影响比较大——至少在早期,这种性能影响完全不能忽略。
通过 y=ax+b,(0<=a<=1) 可以得到以下推论:
当已知直线上一点 (x0,