CSS3DRenderer:元素周期表
- Intro 使用CSS3DObject创建周期表卡片,组合成不同的3D形状
- Render: CSS3DRenderer
- Controls: TrackballControls
- minDistance: 500
- maxDistance: 6000
- Camera: PerspectiveCamera
- fov: 40
- near: 1
- far: 10000
- 初始z: 10000
先不管这个demo里奇奇怪怪的好几种图形变化,
只需要关注几点:
- 最开始时卡片的坐标位置
- 每次变化结束时卡片的坐标位置
- 卡片中间的变换过程
这个例子很像在上体育课时,
学生们在操场上自由活动,这时的位置都是随机的,
一旦体育老师吹哨集合, 学生们归队到原先指定的位置(事先安排好的)
这样就把实现效果的步骤拆分为了以下部分:
- 初始化卡片坐标(随机)
- 计算出卡片在结束后应该的坐标
- 通过Tween实现卡片移动的连续效果
”图形学的大部分工作实际上都花在了数据分析上“ ————谁说的来着
除了scene、camera、renderer、controls这些基础场景元素外,
还有resize、animation等标配函数外,
还有一个最最关键的东西需要准备:元素表数据,
官方demo使用的是一个类似index索引的包含5个属性的完全被打平的数组:
1 | const table = [ |
初始化场景中的实体:
1 | function init(){ |
这里使用的是TrackballControls,里面设置了2个参数:
- minDistance 拉镜最小值
- maxDistance 拉镜最大值
窗口大小resize事件:
1 | function animation(){ |
动画帧函数:
1 | function animation(){ |
在整个变化的过程中,都需要一个objects数组,
这个数组存放所有渲染出来的CSS3DObject实例,
同时还需要一个target数组,
和objects数组中的物体一一对应,
因为target中存放的实际是objects中元素对应的目标点位和旋转角度,
因此可以使用Object3D对象存储
1 | const objects = [] |
初始化的点位采用随机的方式生成,
1 | for(let i = 0; i<table.length;i+=5){ |
其中计算特定卡片在场景中的位置,实际上用到的是:
1 | // col = 总列数 colIndex = 当前列数 cw = 单张卡片宽度 |
球形、螺旋形、栅格形的变换原理也是完全一致的,
不同的是计算的方式:
球形坐标计算
球形坐标系
一个球形坐标系需要3个参数:
- r(radius-半径)
- θ(polar angle - 极角)
- 范围在 0-π 之间
- φ(azimuthal angle - 方位角)
- 范围在 0-2π 之间
这里对球形坐标的计算只使用到每个元素的index索引值:
极角的计算首先将index映射到-1到1的范围之内,
再通过反余弦转换为极角值:
计算极角值时,需要注意,arccos函数的参数范围在-1到1之间,
因此需要现将i索引值相对l映射到-1到1之间
(超出这个范围,Math.acos返回NaN)
方向角的范围需要在0-2π之间,
因此在phi之前需要加一个乘数因子,用于扩大坐标之间的间隔。
1 | // i=元素索引 l=元素总数 |
使用Vector3.setFromSphericalCoords获取到坐标,
1 | object.position.setFromSphericalCoords(800, phi, theta) |
这样得到了坐标点,接下来需要让卡片转向,
实际上就是将卡片坐标P与原点O相连的矢量OP垂直于卡片表面,
可以使用Vector3.multiplyScalar创建一个在OP延长线上的坐标点,
然后让卡片转向它(lookAt):
1 | vector.copy(object.position).multiplyScalar(2) |
最终实现球形映射:
1 | const vector = new THREE.Vector3() |
螺旋坐标的计算
圆柱坐标系
圆柱坐标系的三个参数:
- 镜像距离
- 方位角
- 高度
通过索引计算方向角,
1 | const theta = i * 0.175 |
乘以一个较小的参数用于均匀分布,
接下来计算高度:
1 | const y = (-i*8)+450 |
通过Vector3.setFromCylindricalCoords将坐标转换到柱状坐标系上,
并以和Sphere同样的方式对物体进行旋转:
1 | object.position.setFromCylindricalCoords(900,theta,y) |
最终实现螺旋分布:
1 | for(let i =0;i<objects.length;i++){ |
实际上,使用球形坐标也能实现螺旋的效果,
但是是球形螺旋体:
1 | const phi = Math.acos( -1 +(2*i)/l ) // -1 - 1 |
栅格坐标计算
1 | for(let i = 0;i<objects.length;i++){ |
通过TWEEN实现坐标的转换。
每次切换图形时,都会触发过渡函数,
TWEEN的使用方法非常简单,
类似于Gsap或animate keyframe,
需要将物体的position和rotation移动到对应的位置:
1 | for(let i =0;i<objects.length;i++){ |
变换结束后,需要调用render函数进行重绘:
1 | new TWEEN.Tween(this) |
另外,还需要在最新发起的变换之前,
结束当前正在进行的变换:
1 | TWEEN.removeAll() |
否则,在频繁的变换之后,会出现变换错乱的现象:
最后,需要在帧动画中对TWEEN进行更新:
1 | TWEEN.update() |