使用echarts绘制火焰图

介绍

火焰图常用于性能分析中,显示某个函数堆栈的耗时情况,一般火焰图区块越大,表示耗时越高,越有可能是性能热点,如何读懂火焰图?

绘制原理

echarts官方是没有火焰图类型的图表的,但是可以通过 echarts的custom类型来绘制火焰图;
使用echarts绘制火焰图_第1张图片
火焰图本质上就是一些长方体区块的堆叠,因此我们只需要计算好每个方块的左下角坐标,以及每个方块的宽高,就可以把火焰图绘制出来

主要用到了 custom系列的

  • renderItem函数,用于自定义图形绘制,对series中的每一个数据项都会应用一次
  • api.value,用于在renderItem中获取设置的series中的数据
  • api,coord函数,用于在renderItem函数中计算某个方块的坐标以及宽高

详细文档参考官方链接https://echarts.apache.org/zh/option.html#series-custom.renderItem

绘制

以下是关键部分代码,也就是renderItem函数

/*
在我的原始数据中,记录了每个函数的调用起始时间偏移量(可以理解为函数调用相对于基准时间的差值),调用耗时,堆栈深度,以及其他数据
绘制过程中主要就是使用 时间偏移量计算在图表中的x坐标,用深度计算y坐标,用耗时计算区块的宽度, 
设置的数据格式是
 series:{
 	data:[
 		[offset,timeSpan, depth,xxx],
 		[offset,timeSpan, depth,xxx],
 		[offset,timeSpan, depth,xxx]
 	]
 }
*/
renderItem(params,api)
{
	//整个流程是在vue组件中的,所以这里的this指向的是当前vue组件
	//这里使用maxDepth是因为我绘制的火焰图是倒过来的,深度小的在上面,深度大的在下面
	const maxDepth = this.maxDepth; 
	const minDepth = this.minDepth; //minDepth其实没用到,不写也可以
	const offset = api.value(0);
	const timeSpan = api.value(1);
	const depth = api.value(2);
	
	const start = api.coord([offset, maxDepth-depth]); //y坐标倒一下
	const end = api.coord([offset+timeSpan, maxDepth-depth]);
	const normalSize = api.size([1, 1]); //获取单位长度
	const height = normalSize[1];
	const width= end[0]-start[0];
	//如果数据量太大,可以把一些宽度过小的图元过滤掉,减小压力,当然,也可以在数据源进行过滤
	if(width<1) 
	{
		return
	}
	//这里创建一个长方体图元
	let rectShape = echarts.graphic.clipRectByRect({
		x: start[0],
		y: start[1]-height/2,
		width: width,
		height: normalSize[1],
	}, {
		x: params.coordSys.x,
		y: params.coordSys.y,
		width: params.coordSys.width,
		height: params.coordSys.height
	});


	//var style={ //这里可以自定义填充颜色
	//	fill: option.color[parseInt(width%option.color.length)],
	//}
	//if(depth
	//{
	//	style.fill= '#808080';
	//}
	//if(depth === criterion.depth && offset === criterion.offset)
	//{
	//	style.stroke='yellow'; //边框颜色以及线的属性也是可以配置的
	//	style.lineWidth=2;
	//}

	return rectShape && {
		type: 'rect',
		shape: rectShape,
		//style: api.style(style),
	};
},

缩放

火焰图是可以交互的,点击某个区块可以进行缩放,这个可以使用echarts自带的dispatchAction来发送dataZoom事件实现,通过设置 dataZoom的startValue和endValue进行缩放

this.chartInstance.dispatchAction(
{
	type      : 'dataZoom',
	startValue: dataItem.offset,
	endValue  : dataItem.offset+dataItem.timeSpan,
});

需要注意的是,在缩放后,可能会出现边缘有部分区块是没有被完全过滤掉的,正常情况下不会有问题,但是如果你使用动态计算火焰图区块上名字的宽度的话,就会出现问题,
可以在renderItem中通过不绘制的方法进行手动过滤

 if(offset>=endValue || offset+timeSpan<=startValue)
 {
    
 	return}

动态计算区块中函数名的宽度的方法可以查看 js获取字符串像素宽度,通过动态计算字符串宽度,在 图表的 series-label-formatter中动态显示字符串

series:{
	label:{
		formatter: function(param){}
	}
}

效果

除了配色辣眼睛外,其他的功能都实现了,配色算法研究中。。。

有什么问题欢迎评论留言一起探讨!

你可能感兴趣的:(js,可视化,前端)