解决echarts使用renderItem自定义图表时的残影问题
Posted 索美不达米亚
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决echarts使用renderItem自定义图表时的残影问题相关的知识,希望对你有一定的参考价值。
公司有一个需求是绘制人员的排班任务甘特图,因为有大量自定义元素和复杂交互,在初版时采用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方法里自定义绘制甘特图元素,因为每一行的元素非常多,所以只显示当前datazoom时间范围内元素。
// 计算视口内横向的显示图形 var slider_x = this.myChart.getModel().option.dataZoom.filter(it => it.id == "slider_x")[0] var sliderXLeft = new Date(slider_x.startValue) var sliderXRight = new Date(slider_x.endValue) // 根据视口的范围筛选元素 ...
这时候碰到问题: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\'
};
}
这样设置了以后,大量元素滚动也非常顺滑,解决问题
以上是关于解决echarts使用renderItem自定义图表时的残影问题的主要内容,如果未能解决你的问题,请参考以下文章