Three.js-3D关系可视化
记录项目中的一些想法和经验教训。
(MIT)规范
用户数据必须挂在3D对象的userData属性上。
命名
特别要注意3D对象的id属性的命名
关系对象的命名:from_to
实现
CSS2DObject的事件绑定
这次的文本就是用的CSS2DObject绘制的,事件绑定需要这样处理:
1 | |
注意,需要给每一个CSS2DObject对象增加如下CSS属性,否则无法触发其鼠标交互事件:
1 | |
文本竖排显示
设置css样式即可:
1 | |
竖排后,如果想要精确计算其高宽,得延时通过包围盒获取才行:
1 | |
数据解析
程序分为配置文件+解析函数+调用流程
配置文件单独放一个文件。
解析函数单独放一个文件,可以作为通用的工具函数。
调用流程单独放一个文件。
选择一种合适的经典数据结构
比如Node+Edge,还是Tree?先思考好。
后续的操作都是围绕这个展开的。
(TODO)做一次可视化数据结构的分享
程序设计
应该设计若干个Node类
1、解决Node.isNull()这类问题。
2、不同的Node渲染效果、类型不一样(Sprint、SphereGeometry)
MVC还是最经典的
model: 数据处理
view: 渲染
action: 事件
没有类似的设计,代码写成了一大团乱麻。
果然经典还是永流传的。
渲染分离,可重复调用
我之前没弄好这个,结果导致做2D视角时,难以重复调用render函数来解决。
参考C++的模式:方法定义和方法实现分离开
比如HierarchyLineLayout,其方法定义放在HierarchyLineLayout.ts中(不对,这里先定义了,会导致该文件中调用getPositionByChildrenNum方法的地方报错,因为只是定义,并未实现;即使将该方法定义成接口方法也不行):
1 | |
具体的实现放在ViewHelper.ts中:
1 | |
使用该类时,不再引入HierarchyLineLayout,而是引入ViewHelper:
1 | |
这样就可以解决多人合作写一个类,代码冲突的问题;也能解决单个类文件过大的问题。
为什么这种方式不行?
因为顺序的缘故:
定义HierarchyLineLayout类
通过prototype扩展了类HierarchyLineLayout,给其加上了getPositionByChildrenNumber()方法
使用时,实例化了HierarchyLineLayout类
HierarchyLineLayout类里面,又重新定义了getPositionByChildrenNumber属性,且这里没有给其赋值,因此该方法为undefined,导致报错
所以HierarchyLineLayout类文件里面,不能定义getPositionByChildrenNumber()方法。
这样导致的后果,就是扩展文件ViewHelper.ts中有很多红线提示。
一些拆分方案
TypeScript 中,对 Class 进行代码分割有什么比较优雅的 practice 吗? - 糯米PHP
函数式编程可能是个较好的方案。保证函数的独立性,尽量少依赖类属性,通过函数的组合来实现功能。
(重要)没有通过tag简化数据搜索
比如筛选节点和线、文本标签,都是逐个循环,性能很低。
应该基于tag进行分组,这样就方便多了。
事件交互没设计好
应该按照这样的流程:
1、绑定事件:

2、修改状态:

3、监听状态变化:

4、执行逻辑:

算法
递归思想
比如我高亮时获取关联节点。
树
善用数据结构,能极大提升效率和程序质量。
分工
可以按照数据处理、绘图、事件、动画、特效进行分工。
大家对于数据的理解保持一致即可,这是核心。
方案
旋转相机,物体相对视线的位置不变
注意:目前的方案其实是一个不够强大的方案,是建立在视角中心固定不变、y轴不变的情况下的(即场景只能水平旋转,不能上下旋转和平移)。
思路是:
1、根据相机的当前位置和初始位置,计算相机转动的角度;
2、根据这个角度和三角函数、物体的初始位置,计算物体的当前位置
这里有一个要注意的地方:camera的x为负数时没问题,x为正数时则会跟着相机旋转,需要将角度乘以-1
1 | |
问题
(TODO)removeEventListener无法删除事件
1 | |
这是因为changePosition是一个局部函数,我是调用了这个函数的外围函数,每次这个函数在内存中,其实是不一样的内容