动态折线图开发问题汇总
最近在给业务方开发动态折线图,期间遇到N多问题,这里做个记录,就当是技术复盘了,避免下次重复踩坑。
出现问题,先盯着问题看10遍,把问题明确了、想清楚了,把接下去应该如何调试的步骤想清楚了,再去动代码调试。
花在思考上的时间越多,后面花在盲目调试上的时间就越少。
需求列表
折线图
- 找产品经理拿测试数据
- 确定数据结构
- 公司logo的实现(如何解耦?组件化的思维、装饰器模式,参考蔡东的思路),这个也关系到数据结构(这个必须继承+组合,否则之前的组件会受影响没法用)
- 没有logo的时候的展示方案:首字母-业务方自己实现
- (P1)logo是个formatter-整个div都是自定义的
- 数据较少的情况下,动画是否不连贯?需要做一个插值处理
- (P1)文字重叠的解决方案
- (P1)注意下方的图,坐标是反向的;加个reverse参数
- Y轴刻度值是在图表内部显示的
- (高风险,大活儿)缺少tooltip的样式和交互逻辑,是长按还是按上去就出现?-参考ECharts的来,不延迟展示
- 没有滑动跟手(touchmove)的展示交互
- axisPointer的线条是虚线
- tooltip的节流
- (高风险)tooltip没有自动位于绘图区域内部-需要一个边界检测通用方法
- axisPointer没有每条线都绘制
- 两图合一后,0刻度的值和刻度线需要删掉一个才行,trick删除吧
- 所有横线都很长,通过transform: scaleX(1.5);实现
- 0刻度值不显示“亿”字
- 播放过程中,只显示logo,不显示数值,播放完毕才显示数值
- 单独搞个插件文件,做这些trick操作吧
- 隐藏/显示坐标轴和刻度的配置项
- 将写死的chart移出去,增加chart的position检测
- x轴线会被刻度线遮住的问题;每次刻度线变更后
- Y轴0刻度没显示-业务方自己设置显示什么范围、显示哪几个数值
- 小心纵坐标取整的问题,提供一个默认的取整策略
- 横坐标的线宽度是很长的-这个太另类了,业务方自己划一根背景色
- 有个问题,0轴如果用同一个,下面的线条会和X轴刻度值重叠
- 按照UI稿还原样式
- tooltip需要有,回调数据记得排序
- 黑白风格-业务方自己修改吧
- 背景刻度线和grid是虚线
- 圆点的层级不是最高的
- X轴没显示刻度-是我字体设置太小了
- 圆点的颜色是和线条一致的
- (重要)内存泄漏问题
- (重要)各种测试,自己各种使用,参考我们的自测checklist
- 操控栏(播放、重播)
- iOS10和Android5测试
- 无动画模式(默认进来就全部绘制好了)
- 坐标轴的计算,我放在logo.html中了,得移动进代码中
- DynamicLine中的逻辑下移,这里只做流程管控
- 将代码改为游戏loop循环的机制
- 做个在线页面,直接导入数据,生成动态图表,并且可以有一个链接
- 比例尺移出去
- 混乱的数据结构,压根没有数据结构(帧数据结构、整体的数据结构。。。。。。);得改为TS。。。
- 1、我的Debug模式呢?VConsole、Stat.js
- 2、增加通用的SVG和DOM的样式配置工具-抽象为函数,这个很通用的,SVG和DOM有哪些属性上的差异?,我传入一个option,自动生成右侧配置面板!
- 3、上传Excel查看效果
- 4、我的武器库,拆分细粒度单元和算法
- 如何解耦?尽量不用state;将dynamic的this作为参数传递到函数内部,比如scale
- 处理事件交互的不可见矩形也可以移动出去
开发反馈的问题-2022.03.16
- 第一帧会停止一段时间
- tooltip的触发,不是在两帧之间的位置,只要小于下一帧位置就会触发上一帧-反向比例尺的二分查找,没有考虑做中间距离的判断
- tooltip的显示位置,没有和手炒一致,即手位于左边就显示右侧、手位于右边就显示左侧
- tooltip改为手按住一直展示,离开后就马上消失
- tooltip无法选中最后一根-是因为反向比例尺缺少最后一个导致的
- tooltip和axis轴上下互通
- x轴刻度值内侧对齐
- 左侧坐标轴文字的层级问题,会被axisPointer遮住
开发反馈的问题-2022.03.17
- 初始会停止一个周期的时间-去掉playByDate的强制延时
- 两帧之间的文本数值,增加一个插值运算:我又想错了,这个应该是数据和绘制分离,不应该直接在Anchor中写的;还是应该在Line中计算好数据,然后扔给Anchor只负责绘制;我之前有个做得不错的地方,就是数据是作为参数传递给anchor的update方法的,这样解耦了数据计算和渲染
- chart为了两个合一起,设置了一个top值,需要动态计算-将图表的top设置为grid.margin的上下外边距之和即可
- 绘图不从0开始,靠右一点;目前x轴的位置实际上是没对齐的;需要设置4个地方:x轴的axis的offset、endPointer的formatter中的margin-left、series.line.style.transform、postProcess中把X轴0轴线左移
- 中间某一帧的折线会一下子画完了,和playByDate去掉了setTimeout有关系;还是loop的问题-增加50ms延时来解决
- 手机上折线总是突然就出现了,没有动画;会不会是date计算出错了?算到下一个日期了?但是也不会没动画呀;是哪里触发了什么绘制函数么?需要把每个元素的绘制独立开,不然找问题要从一大堆东西里面找,很麻烦,效率极低;感觉是因为计算耗时的问题,手机性能比PC差,算得慢,所以出现这个问题;PC性能好,所以没这个问题;和我的插值可能有关系,改为300ms就没出现了;不过低端手机上可能还是会出现;就第一帧算不赢
是stroke-dashoffset插值后的值变成0了
和我用Promise有关系么?宏任务、微任务?
开发反馈的问题-2022.03.21
- 最后一个点对不上
产品反馈的交互-2022.03.23
1.折线图增加播放器,按年度划分,其中
- 播放器样式参照行业对比,显示初始年份,播放年份。注意点:因为选中股票上市年份为非固定,所以不可按照行业对比一样十等分,需要按照实际年份等分
- 播放过程中可暂停,可前后年份拖拽定位,但拖拽时无过场动画。举例,从2021年拖回到2015年,折现不会回缩,手指松开后点直接从选中年份开始。
- 另页面内“融资展开收起”、“tab切换”、“切换股票的”、拖拽年份后播放停止。操作完成后需要重新点击才可继续播放
2.折线图页面融资象限默认收起,可点击展开按钮后展开,展开后为分红融资双象限模式。分红象限无需展开收起操作。
- 若融资为收起状态下,浮窗仅显示分红数据,如“2010年分红”,无需展示融资数据
- 派现能力排名TAB下优先展示“累计净分红”(“派现融资比”可通过右上角tab调出),在“累计净分红”下添加播放组件。播放器默认年限上限为10年,不足10年按照实际年份等分。“派现融资比”无需加播放器。播放时未上市股票显示“未上市”,同行业对比一致。
- 加入Timeline控件
- 封装为一个更高层的组件
开发反馈的问题-2022.03.25
- X轴0轴遮挡住了文字-给文字元素设置个positon:relative,然后设置下z-index即可
- 数值比较集中时,5次碰撞检测不够:是0轴的文字重叠了,沃日
- 初始几个周期没数据的点,也会绘图
- logo的示例,碰撞检测的结果不美观,应该居中才合适
- 程序设计有问题,动画播放过程中如何直接调用playByDate()跳转到其他地方?
开发反馈的问题-2022.03.26
- 指南针这只股票的碰撞检测还是不对-是不是Y比例尺计算有误?怎么指南针跑到0轴下面去了?下午去隐藏下面的线看看,是比例尺不对,还是div样式有问题,还是换行了?
- anchor圆点没了
- 轴线把折线遮住了:层级问题出现很频繁,得有个设计原则来解决!应该设计**生命周期(看下Unity的那个生命周期图)**,明确在框架画完的时候,再设置顺序!等等,我记得这个顺序只针对同一个父元素下有效,那么我得设置父元素的才对。
不对,这是因为两个图叠加导致的,这个似乎没法改了;可以修改,将第二个绘图的div设置z-index就可以了。
X轴0轴虚线和实线重合了,应该是没有加postProcess
pointer在跳转到具体某个日期后,还会播放动画
开发反馈的问题-2022.03.29
- 组件不支持传入dom对象,同一个页面绘制多个图形时会出问题
- 暂停的时候,也需要增加碰撞检测,否则文本重叠很严重
- 股票名称带星号,会报错:这是因为我把股票名称作为css选择器的一部分了;改为转换为ASII码
开发反馈的问题-2022.03.30
- 2.7.1引用报错:是因为我build的时候给js文件名加了hash值,导致找不到入口文件了
- 数据只有一个时报错:程序没判断,直接取了frameData[1]
- 线条跑到绘图区域外面去了
- 播放完毕后,再点播放,之前的线条没消失:没有清除
开发反馈的问题-2022.03.31
- Y轴的刻度线没法延长和删除了:是因为动态变更Y坐标轴后,没有在每次轴范围变更后删除线条和延长线条;这个无需修改组件,修改postProcess()的调用时机即可
- 柱状图的右下角label和滑动条的pointer样式不对:是因为我写了很多通过id定位的情况。。。
- 还有一个季报不支持,导致我们这边梳理的宏观数据出现1年对应多个数据的情况无法展示。我们现在只能搞一个GDP的。
- 折线图点击上一期下一期,文字会重叠:因为没有判断数据缺失的情况
- 柱状图绘制多个图表时,defaultDate不生效;后面的timeline的update会影响前面的对象
开发反馈的问题-2022.04.01
- 柱状图的tooltip无法显示
- 柱状图的北美风格的名称位置如何调整?
- 时间线的刻度,已经播放完的刻度线样式可以自定义
- 折线图在PC下的tooltip的定位模式需要修改下
- 删除axis的ticks的line,会导致播放后全部ticks都没了:
陈锦宏反馈的:
1.配置坐标轴和折线使用axis和series数据结构和传统echarts类似,比较清晰。
2.两个图表叠加问题。在echarts是可以通过配置在一个dom实现。目前的话是通过两个dom绘制两个图表再通过d3api去合并。使用d3的api会提升
使用成本。
3.timeLine这个对象内置会好一点,现在每次重新绘制也要再初始化一次timeline,这个放在内部更新好点。
时间轴向外暴露事件即可不需要绘制dom的api,可以利用webcomponet在外部写一个通用的组件作
参考。总结:目前如果单个图表是能通过配置项解决,但叠加图的话有点像是业务人员在外拓展,定制化。如果可以的话利用配置项向外拓展功能。
时间轴的初始化可以在内部消化,不需要在外部调用。时间轴向外暴露一些基本的数据和事件。时间轴UI层提供通用的webcomponet组件。不要混在图表功能内。
开发反馈的问题-2022.04.02
只有一条线,且前面几个值都是0的时候,报错了
不支持常规日期,只支持数字年份
timeline优化
- playing的状态,不应该由timeline关注,应该组件自己搞定这个播放中的状态切换操作,即可以从任何一个周期的任何一帧,切换到另外一个周期的动画
- 队列应该移动到组件内部,不是timeline维护
让杰哥慢慢接手起来吧,我腾出时间,不然要GG了
柱状图
- 支持所有公司都展示logo(不改,前端反馈这个组件和和行业对比保持一致,不做额外样式)
- 右侧显示logo
程序设计
灵魂拷问
你设计分组了么?
你创建g对象了么?
你的位移是操作g对象么?
数据结构
先确定名词概念:
- 周期
- 帧
- 循环loop
- 周期数据
- 常量定义
折线图
不合理的地方:
Line和Area不应该只有一个,应该是有多个实例。
数据管理应该放DynamicLine中,包括回调参数的管理
归根结底,还是数据和渲染没分开的缘故;应该在DynamicLine中处理好所有数据才对
动画部分单独抽离出来
chart元素需要设置position:relative,然后将用户自定义元素设置为position:absolute
tooltip和axis轴上下互通
- 暴露axisPointer的显示和隐藏方法,参数是date
- 用户可以自己注册touchmove事件,装饰器模式
- 只显示第一个图的tooltip,第二个图的隐藏掉
- 禁用默认的tooltip事件,完全由画布自己注册事件,传入事件event,通过event获取x坐标,调用axisPointer和第一个tooltip
- axisPointer
待解决的问题
廖造光:
1 | |
不能做的事情
发布NPM包不能加hash后缀
组件打成NPM包之后,加载是根据package.json中的main配置的路径去寻找入口文件的,因此build时加上hash值,就会导致组件找不到入口文件。
现在改为新起一个script,专门用于发布npm仓库时的打包。
可操控的时间轴类组件,动画不应该放在具体组件层
否则用户在动画播放过程中操作,你就很难处理了。
不能通过唯一ID或者样式去选择DOM元素
因为用户实际应用场景,可能存在一个页面绘制多个图形的情况,比如结果页。
时间轴不能用线性比例尺,必须用scalePoint
否则无法适应季度、日期等形式
经验总结
移动端事件和PC端的差异
touchmove和mouseover
移动端获取touch的坐标:
1 | |
PC端:
1 | |
数据计算和渲染分离
在做两帧之间的文本数值时,我又想错了,这个应该是数据和绘制分离,不应该直接在Anchor中写的;还是应该在Line中计算好数据,然后扔给Anchor只负责绘制;我之前有个做得不错的地方,就是数据是作为参数传递给anchor的update方法的,这样解耦了数据计算和渲染
后来我在Line中做处理,10分钟就搞定了。。。。。
有时候效率太低,是方法和程序设计的问题。
有业务方使用和测试的阶段,基本做不了其他事情,都在答疑和解决BUG
想办法弄一下,不然没法专注做事。
今天从中午到下午5点半,自己计划的事情,啥也没写。
复现业务方的问题!!!!如何快速拿到他们的数据和配置?????????
不能复现效率太低了,要把老子整死了。。。
流程梳理
- UI提供视觉稿
- 开发确定数据结构,整理Excel模板给产品经理
- 产品经理导出各种数据给开发
案例分析
引用传递导致出问题后很难排查,因为关联影响太多了
我用了太多引用传递了,而且还到处修改这些属性的值。
比如比例尺。
一旦出问题,就像打地鼠一样,很难排查;稍微修改点东西,也不知道会不会其他地方又受到关联影响,进而出错了。
程序写得好不好的判断依据之一,就是耦合度、复杂度的控制是否优秀。
3.30日排查F10问题耗费几个小时
绝大部分时间都花在了搭建环境上:
我去F10搞了几十分钟,因为我的代码是NPM中的,难以调试,结果一无所获。
然后让F10同事把数据和配置项上传到我的代码库,又过去了1小时
我再根据F10上传的内容还原页面,花费20分钟
调试问题花费大约40分钟
前面三步都是环境还原,后面一步才是调试,两者都可以极大优化。
比例尺初始设计是周期级别的,后面动态Y轴要求帧级别的
导致基本推翻重写比例尺。
另外因为之前柱状图已经在使用了,现在得维护两份了。
Timeline和图表耦合度太高,后面只能通过Timeline2来重写
又得维护两份了。
自己控制每一帧动画时,务必做好最后一帧的补帧操作
否则会因为最后一帧无法到达,而缺少部分绘图。
不对,d3的transition有end回调,已经考虑到这一点了。是我自己写错了,end中没有扫尾操作吧?
比例尺类型的选择,针对同一个维护,比如x轴,有些时候可能需要2个比例尺
比如x轴因为我们交易日可能是非连续的,所以不能用Linear比例尺;但是用Point比例尺的话,计算过渡动画的位置时,无法算出两个X坐标之间的数据,因此还得增加一个两个数据之间的小范围的线性比例尺:
1 | |
这样就可以计算两个周期之间的过渡动画每一帧的X坐标了。
最终,我总共写了5个比例尺:
1 | |
过渡动画中每一帧的终点数据的补值
x和y都需要对两个动画之间的过渡数值做手动的计算,算出当前tick最后一个数据的date和value值,类似这样:
1 | |
逻辑顺序导致的BUG
最好先做方案设计,把计算逻辑和计算流程给画出来。
比如这里的顺序,一旦放错了就会出问题了:
1 | |
没有测试异常数据的情况
包括:
- 负数值
- 部分周期的数据缺失
- 同一周期的不同数据,量级差异巨大
删除刻度的g下面的line,下次call不会重新绘制line的问题
怀疑是:
1 | |
这个如果发现tick没了,就会重新绘制;如果发现tick还在,就会重复利用。
所以:
1、我直接删除class=tick的g,然后call(axis),发现没有tick了,就重新绘制了,所以下一次仍然是全的tick
2、我只删除line,下次call(axis)时,发现tick是有的,就不再重新创建tick的g了,因此g内部的line也不会创建,就没有了
没有考虑到只有一条线、前面数据全为0的情况
导致比例尺的最大值为NaN了
这种边界判断,是我的弱项。
不支持季度、日期格式的date
柱状图本身似乎没问题,主要是时间轴?
因为时间轴不是线性的?错了,是因为我用了++的缘故,这样季度++肯定不等于下一个季度啊
- 时间轴的刻度值画得太多了:应该是用了线性比例尺,改为Point比例尺即可;timeline的入参要修改下,改为传入所有数据,排序后的所有数据;另外我不该对传入的x日期格式做任何处理;数字、文本,都应该是可以的;顺序也是可以自定义的才对
- 柱子右侧的文本全部都是0
- X轴的轴线数据也是0
弱类型语言带来的大坑
比如用户传入的date,可能是字符串,也可能是数字。这样我各个地方的判断就会很坑,比如这个:
1 | |
dates是从用户的数据中筛选的date,可能是字符串;onStep回传的date则被我处理成数字了,这样就导致在dates中找不到这个date,dateIndex=-1了。
这里其实我不应该将date转为Number,这样限制了用户的自由度,比如“2022-04-05”这样的日期就没法用了。
必须要增加更加完善的类型校验,组件内部一定要用TS来写!
常见问题的排查方法
线或者anchor位置不对,飞出去了
这种情况一般是Y坐标值算出来是负数,基本都是比例尺不对,包括:
- 比例尺没有正确在transition中变更,导致用了上一次的比例尺,范围是上一次的,比较小, 所以这一次的数据算出来后,点的坐标都成为负数,跑到最上面去了
部分线条没画出来
可能是数据对应的color没配置导致的
defaultOption的闭包导致的问题
应该通过工具排查问题,比如Chrome的debugger断点,可以查看闭包情况。
另外debugger可以一步一步跟踪变量情况,这样效率才最高。
TODO LIST
感觉4月份能搞定这些就不错了。
这就是积累“二十年的功夫”,所以这不是一朝一夕可以完成的,得持续改进。
清明节要搞定的
照着Games104的第三课抄!
ECS架构:https://zhuanlan.zhihu.com/p/30538626
我先把ECS的关系图画出来,先想清楚通信机制,以及状态机,再去实现。
- 【Warning】组件和Timeline的X轴都不支持季度、YYYY-mm-dd格式的日期
- PC端的timeline的事件bug
- 柱状图的tooltip无法显示
- 柱状图的北美风格的名称位置如何调整?
- 时间线的刻度,已经播放完的刻度线样式可以自定义
- 折线图在PC下的tooltip的定位模式需要修改下
- 负数情况的虚线框展示功能
- 人口金字塔的方案设计
- TickLogic和TickRender没有抽象出来,应该放在基类中
- 数据结构没整理出来
- 独立出DataSet类
- debug模式改为url传参来判断
- 【Warnning】line.js中,默认把两个x之间的间隔时间写死为1年了
- Demo文件整理下,太乱了
- 没有做到数据和视图完全分离
- enter、update、exit没应用好(压根没应用)
- 项目结构优化下,比如data的加载、访问地址不应该有dist,清理无用的文件;参考下vue的项目
- 【Warning】自定义的内容中如果存在图片,每一帧都会去重复加载:要么将logo和文本分开成2个配置(这个似乎好一点,对应用方更方便);要么想个不每次重新创建dom的方法
- tooltip的定位,需要更多的类型,可以是跟随触摸点,也可以是固定左右
可以靠后处理的
- 碰撞检测不理想
- Y轴0轴要不要靠底部,需要改为通过配置项来设置;目前写死了
- 动画interval设置过短时,前面的比例尺和动画的transition不执行或者掉帧的问题
- 封装为vue组件,方便接入LowCode平台
- 改为TypeScript
- 底层绘图库不可切换(d3应该只做计算,底层可以切换到ZRender)
- 用map、reduce等等简化代码
- 计算移入webworker
- 单元测试
- 支持CDN形式的引用
- 代码生成器/框架生成器
- 引入vite