如果我来做,我会怎么实现? 页面展示的内容 首先对日历的展示内容做一个拆解,大致包括:
主标题(比如年份)
子标题(比如月份)
日期的整体容器格子
单个日期的方格子
单个日期的具体显示内容(设计时考虑到高扩展性)
数据结构 日历数据 1 2 3 4 { "date" : "20200527" , "value" : 30 }
配置项
语言(中英文)
布局(横向、纵向)
显示内容的自定义函数(formatter)
页面上每个元素的样式配置信息、位置配置信息
事件
具体实现的解析 程序如何识别当前的配置是个日历图? 如何自定义每个表格的内容? 可以参考我们新扩展的自定义类型来实现。
散点图是怎么画上去的? component下的calendar的代码,并没有实现散点图的功能。
这个是配置在series下面的,应该是每个组件都是和series是合作作图的,组件会将既定的位置用series设置的图形进行绘制。
那么这个结合具体是怎么实现的呢?
在CalendarModel.js中,有一个calcPoint方法,用于计算每个日期的位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 calcPoint (date ) { let { days } = this ; let cellSize = this .get ('cellSize' ); let left = this .get ('left' ); let top = this .get ('top' ); let finded; date = new Date (date); finded = find (days, function (day ) { let d = day.d ; return d.getFullYear () === date.getFullYear () && d.getMonth () === date.getMonth () && d.getDate () == date.getDate (); }); if (finded) { finded.x = left + cellSize * (finded.xi + 0.5 ); finded.y = top + cellSize * (finded.yi + 0.5 ); return finded; } }
该方法在src/chart/scatter/ScatterModel.js中的updateByCalendar()方法中会被调用(EffectScatterModel继承自ScatterModel,所以这个方法也会被EffectScatterModel调用),计算出每个元素的位置后,挂载到model.points属性上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 updateByCalendar ( ) { let { globalModel } = this ; let data = this .getData (); let calendarModel = globalModel.getComponentByIndex ('calendar' , this .get ('$calendarIndex' )); let points = []; each (data, (d, i ) => { if (d) { let date = d[0 ]; let point = []; let _point = calendarModel.calcPoint (date); if (_point) { point = [_point.x , _point.y , d, i]; } else { point = null ; } points.push (point); } }); this .data = data; this .startIndex = 0 ; this .lastPoints = this .points ; this .points = points; }
然后在EffectScatterView中,就会用到这个定位信息,去画图了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 renderAllSymbol (model, globalModel, global , showPoints ) { let group = this .group ; let symbolOption = model.get ('symbol' ).normal ; let { isScatter } = model; let cursor = model.get ('cursor' ); let attrs = []; each (showPoints, function (point, index ) { if (point) { let itemData = point[2 ]; let attr = { shape : getScatterShape (model, itemData, false ), position : [point[0 ], point[1 ]], style : getScatterStyle (model, itemData, false , point[3 ]), key : point[3 ], seriesIndex : model.index , dataIndex : point[3 ], rectHover : true , cursor, model }; attrs.push (attr); } }); this .setShapeGroup ('symbolGroup' , EffectSymbol , attrs); }
如何做事件监听? 这和之前的事件监听是一样的,绑定到series即可:
1 2 3 4 let seriesModel = chart.getModel ('series' , 0 ) chart.registerAction (seriesModel, "ITEM_CLICK" , function (e ) { console .log (e) })
一些技巧 先确定布局和数据结构,并制定见文知意的英文名 这样你对于该怎么设计代码中的数据结构、配置项,就会很明确了。后续和别人交流起来也因为定义好了名字,更容易沟通。
给你拆分好的每一类页面元素,都设置对应的样式配置项 这样可以保证你不会遗漏,也会最大化页面表现力的自由度。
将页面上每个元素的渲染独立开 比如类似这样:
1 2 3 4 5 6 7 render (model, globalModel ) { this .renderDays (...arguments ); this .renderSplitLine (...arguments ); this .renderDayLabel (...arguments ); this .renderMonthLabel (...arguments ); this .renderYearLabel (...arguments ); }
将所有绘图操作交给绘图层ZRender 如果你在view中有操作DOM元素的代码,那说明你肯定用错了。
view中只应该有操作属性和配置ZRender既有元素(比如Text、Rect、Polygon等)的代码。
用组合的方式拆分页面元素 比如calendar和series的拆分,就是一个很好的例子,这样扩展性强,表现力更好。