公司有一個需求是繪制人員的排班任務甘特圖,因為有大量自定義元素和復雜交互,在初版時采用dom+Virtual List的辦法來做,顯示和交互的效果都不錯,但是一旦數據量大的時候,就算是Virtual List在滾動時都會很卡,於是就有了通過echarts使用canvas來繪制甘特圖的想法,主要是通過echarts的renderItem自定義圖表來展示,於是開始踩坑。
option
yAxis: { type: 'value' }. xAxis: { type: 'time' } series: [ { id: 'flightData', type: 'custom', renderItem: this.renderGanttItem, dimensions: [null, { type: 'time' }, { type: 'time' }, { type: 'ordinal' }], encode: { x: [1, 2], y: 0, }, data: echarts.util.map(_missions, (item, index) => { let startTime = new Date(this.root.options.startTime) let endTime = new Date(this.root.options.endTime) return [index, startTime, endTime].concat(item); }), }, { type: 'custom', renderItem: this.renderAxisLabelItem, dimensions: [null, { type: 'ordinal' }], encode: { x: -1, // Then this series will not controlled by x. y: 0 }, data: echarts.util.map(_staffList, function (item, index) { return [index].concat(item); }), } ], dataZoom:[ { id: 'slider_x', type: 'slider', xAxisIndex: 0, filterMode: 'none', height: 20, bottom: 0, start: this.zoom.x_left, end: this.zoom.x_right, handleIcon: dragIcon, handleSize: '80%', showDetail: false, backgroundColor:'#E4E7ED9E', throttle: 100 }, { id: 'slider_y', type: 'slider', filterMode: 'weakFilter', fillerColor:'#d2d9e4', yAxisIndex: 0, zoomLock: true, width: 20, right: 0, start: this.zoom.y_top, end: this.zoom.y_bottom, handleSize: 0, showDetail: false, backgroundColor:'#E4E7ED9E', throttle: 100 }, { type: 'inside', id: 'insideX', xAxisIndex: 0, throttle: 100, zoomOnMouseWheel: false, moveOnMouseMove: true }, { type: 'inside', id: 'insideY', yAxisIndex: 0, throttle: 100, zoomOnMouseWheel: false, moveOnMouseMove: true, moveOnMouseWheel: true } ]
這時候碰到問題:renderGanttItem返回的元素group數量是變化的,因為時間軸的移動,顯示的元素在不停變化,元素數量當然在變化,echarts對於減少和新增的元素,在界面上會出現殘影、動畫跳動的問題。參考了文檔嚴格定義了唯一的series-custom.renderItem.return_rect.id也不能解決。
解決辦法一
通過設置series-custom.renderItem.return_rect.ignore,節點是否完全被忽略(既不渲染,也不響應事件)。當視口離開元素時,把元素的ignore設置為true,當元素一定出現添加和減少時,先 調用this.myChart.clear(),保證不會在renderItem中有元素增減。
這個辦法有用,但是碰到每行元素很多的時候,就算時設置了ignore,也可以看到卡頓,沒有canvas對比dom的流暢,只能修改dataZoom-slider.throttle增加節流來減少卡頓。
解決辦法二
通過查看源碼,看到這樣一段注釋:
// Usage: // (1) By default, `elOption.$mergeChildren` is `'byIndex'`, which indicates that // the existing children will not be removed, and enables the feature that // update some of the props of some of the children simply by construct // the returned children of `renderItem` like: // `var children = group.children = []; children[3] = {opacity: 0.5};` // (2) If `elOption.$mergeChildren` is `'byName'`, add/update/remove children // by child.name. But that might be lower performance. // (3) If `elOption.$mergeChildren` is `false`, the existing children will be // replaced totally. // (4) If `!elOption.children`, following the "merge" principle, nothing will happen. // // For implementation simpleness, do not provide a direct way to remove sinlge // child (otherwise the total indicies of the children array have to be modified). // User can remove a single child by set its `ignore` as `true` or replace // it by another element, where its `$merge` can be set as `true` if necessary.
在renderItem返回的group元素中設置$mergeChildren='byName',並且給每一類元素設置一個name,這樣每次都會根據name來更新元素
renderGanttItem = (params, api) => {
...
return {
type: 'group',
name: 'gantt-group',
id: categoryIndex,
info: {data:item},
children: allChildren,
$mergeChildren: 'byName'
};
}
這樣設置了以后,大量元素滾動也非常順滑,解決問題
