开发笔记-Insight的文本与曲线布局
需求
现有实现的源码阅读
方案设计
Insight 类型的,标签最多 3 个:最大、最小、异常值
如果是显示事件类的,数据量就可能很多,而且当前 F10 的事件没有权重的属性,无法根据权重排序筛选
流程
Model:把用户的配置项转换成 echarts 配置项
由于这次仍然是用的 dvMarker,因此 Model 不用修改,直接完全沿用之前的即可。
调用 flag 初始化图形元素
模仿 flag 新增一个 annotation,这个工作量比较大。
计算 flag 中 point 和 label 的相对位置
算出 flag 的包围盒矩形
算出 2 个曲线点的坐标
flag 的返回类型 markerRenderItemReturn ,就是 echarts 的自定义类型:
1 | |
处理布局信息
只需要算 Y 轴的布局,直接全部沿用 flag 的布局,无需修改
这里有个问题:Y 轴移动后,整体的包围盒大小也是会变更的!
先不考虑这个,减少变量,不然碰撞无法静止下来。
算法
概念

P: Point 数据点的坐标
L: Label 文本标签
C: Center 画布中心点
新增的配置项
xGap:point 和 label 在 x 上的空隙,单位像素
yGap:point 和 label 在 y 上的空隙,单位像素
linkDirection: 线和 label 的连接点方向,horizontal、vertical
限制条件
数据点的 X 是不能变动的,这意味着单个注解的一侧的 x 坐标肯定是固定的
程序流程
1、内存中绘制文本 Label,计算其高宽
2、根据数据点 Point 距离画布中心 C 的位置,确定该 Insight 的文本 Label 相对数据点的绘制方向
1 | |
3、确认每个 Insight 的包围盒(Pint + Label)的大小
4、因为限制条件的缘故,X 不能动,因此调整 Y,步进算法解决重叠问题,注意设置 step 次数上限
5、仍然排不下,则根据权重,隐藏不重要的 Insight
开发准备
定义数据结构
构建开发环境
把 VISALL 最终生成的数据拷贝一份过来
SD 里面搞个 Demo 页面
找到获取所需数据的各个 API
获取文本的内容
计算文本的尺寸
计算文本+数据点的包围盒
获取画布中心点的坐标
获取画布的左上和右下的坐标(是否可以先用相对位置来计算)
Takeaways
(精)通用程序设计套路:围绕生命周期进行
prepareXXX():数据预处理,将用户传入的数据和配置,转为程序内部的数据结构和存储方式
init():初始化,如果是 View 相关的代码,一般会包括绘图、事件监听等
update():数据驱动变更,注意这里一般只处理纯数据,不涉及绘图
destroy():销毁
(精)新增一个 Series 的流程
注意,代码全部都应该写在
extension目录下。
新增图元:
通用图元在 src\extension\graphic 目录下,可以参考 CurveLine.ts;
非通用图元放在具体的 series 目录下,比如 src\extension\series\dvTwoWayTree\shape.ts
数据处理:参考 src\extension\series\dvTwoWayTree\TwoWayTreeSeries.ts
布局计算:参考 src\extension\series\dvTwoWayTree\twoWayTreeLayout.ts
获取数据点的坐标
1 | |
获取画布的包围盒
这是页面的像素坐标:
1 | |
获取文本包围盒
1 | |
如何新增图元
可以参考这次宇航实现的曲线:src\extension\graphic\CurveLine.ts
注意,新增的图元,需要先注册才能用:
https://echarts.apache.org/zh/api.html#echarts.graphic.registerShape
布局方法里面,获取的矩形框的宽度是负数,这是为什么?
宽度正负表示左右朝向,高度正负表示上下朝向
当前 dvMarker 的布局逻辑是什么样的?
把所有要布局的元素放到一个数组里面
根据设置的边距,确定文本标签可摆放的区域(一个矩形?)
遍历这个数组,摆放元素
如果放不下,则减小元素之间的间隙,然后再次摆放
如果重复 N 次还是放不下,则隐藏权重低的元素
如何调试 ZRender 元素?
经验教训
没弄清楚全流程,盲人摸象
一开始我看了\src\extension\series\dvMarker 目录下的代码,以为这就是全部逻辑了,结果后面才发现,这只是布局相关的代码,而数据处理的逻辑在\src\model\series\dvMarkerSeries\DvMarkerModel.ts 里面(把用户的配置项转换成 echarts 配置项),真正到了 extension 下面时,其实各个子元素的定位数据已经计算好了。这对于我理解整个流程和设计方案造成了困扰。
数据驱动、数据驱动,一定要先弄清楚数据流转的全过程,再去看代码。
定义好数据结构
数据结构如果没定义好,会导致概念多,增加了理解成本,有点乱。
技术储备不足导致的问题:方案质量 VS. DDL
这次本来有个从程序设计上来看更好的方案,就是扩展 ECharts,增加一个类似 markerArea 这种控件。但是这个涉及的概念和时间成本太多了,因此最终还是选择了根据现有的程序设计并不好的 dvMarker 进行修改。
TODO
阅读 MarkArea 相关的源码,比如 MarkAreaModel.ts,参考数据处理的方案
阅读 LabelLayoutHelper 相关的源码,参考碰撞检测方案(
shiftLayout方法)
ECharts 官网的动态排序折线图的案例,就应用了这个碰撞检测,可以用这个 Demo 调试下代码。
特别注意下这几个:
prepareLayoutList 方法:数据预处理
balanceShift 参数:是否在所有标签上平均分配偏移量
squeezeGaps 方法:上下留出边界,美观
delta 参数:可以往外延伸的像素值,大于 0 上边界,小于 0 下边界?
- 参考 MarkArea 内容,扩展成一个新的 ECharts 控件。
资料
基于 dvMarker 的标签功能:
https://datav.iwencai.com/example.html#/static-page?id=60
Matter.js:一个 2D 物理引擎,可以用来做碰撞检测: