在探索如何实践的最佳方法时,我们想出了一个十分有趣的原型:从一个充满颗粒的旋转球开始。
See the Pen render-3d-in-2d-canvas by yuchenyao (@yuchenyao) on CodePen.
从技术角度出发,第一步是最有趣的。因为接下来所有的动画步骤都是基于平面2D,不能使用3D渲染器,例如Three.js。因此,我必须搞清楚如何只用 Canvas 2D API 来渲染3D图像。
在这篇文章,我将向你们展示我是如何做到的。首先,我要解释一下如何使用JavaScript Canvas 2D API来渲染一个3D场景中的基本图像。接着,在文章的第二部分中,我将向你们展示如何用使这些变得更有趣。
1. 创建Canvas画布
准备开始前,我们需要在HTML中新增一个canvas元素。我们还要为元素创建一个ID以便于选择。这些就是在HTML中要做的所有工作!
|
|
对于CSS,我们需要去除body的默认margin,并且用overflow: hidden;
来阻止滚动条的显示。因为我们想让canvas铺满屏幕,因此我们把它的宽高定义为可视窗口的100%。
|
|
为了在JavaScript中创建一个满屏的canvas,我们需要从DOM中选中canvas,然后获取canvas 2d 上下文。
|
|
2. 创建颗粒
我们的目标是用大量的点创造一个球体,因此我们需要在球体表面计算坐标。球体表面坐标的计算不再使用经典的笛卡尔坐标系(x,y,z),而是采用极坐标系中的三个值:
- Radius 半径
- Theta 球心与球面一点的连线与z轴的角度 [0, Pi]
- Phi 球心与球面一点的连线在xy平面的投影与x轴的角度 [0, 2Pi]
在我们的例子中,我们希望每个原点都是随机的,因此我们在创建原点时,要将Theta和Phi定义为随机值。每个原点的半径都一样,我们可以把它存为全局变量。
关于球坐标与三维直角坐标的转换看这里。
|
|
|
|
为什么使用Math.acos()创建 PHI 的随机值?
|
|
因为点在球体表面的分布会不均匀,在球的两极会分布更多的点。
创建 Class Dot
为了很好的管理大量的颗粒,最简单的方法就是使用Class。Class允许我们为每个颗粒定义随机的属性,并且让它们之间共享通用方法。
创建Class的第一步就是constructor方法,我们用它来存储每个颗粒的自定义属性。接着为小圆点创建两个方法:project()
和draw()
。project()
就是奇迹发生的地方,在这里我们把颗粒的3D坐标转换到了2D世界。最后,我们计算得出projected valued
之后, draw()
帮我们在canvas上画出颗粒
|
|
3. 场景渲染
现在所有的颗粒都做好了渲染到屏幕的准备。接下来我们需要创建一个简单的方法,遍历所有的小圆点,并将他们渲染到canvas上。
|
|
4. 3D到2D的转换总结
- 首先要随机生成大量的圆点。固定球体半径,随机生成Theta和Phi。然后把球坐标(r,theta,phi)转换为三维直角坐标(x,y,z)。
|
|
- 因为球体是横向转动,因此球体上的点在y轴上的值并未改变,我们只需要考虑x轴和z轴,此时俯视整个坐标轴。我们需要计算出点的运动轨迹,并在canvas上画出,这样所有的点一起转动便是一个旋转的球了。
坐标旋转变换
|
- 原点在z轴上越大,视觉上距离我们越近,在z轴上越小,视觉上距离我们越远。