[TOC]
参考资料
ECharts 自定义系列 profile - ECharts
背景
希望将 ECharts 自定义系列官方提供的 profile 图表,横纵坐标进行转换。
实现效果
关键点
series-custom.encode
encode 的作用是映射
。
简单的说,就是从一个二维数组中,选择需要映射那几列数据到图表中。本例中,encode 从 data.value 中映射数据。
option = {
series: [{
encode: {
x: 0,
y: [1, 2]
},
data: data
};
数据映射,有两层意思。
- 选择使用哪些数据
- 被选中的数据映射到画布上(类似于地图的比例尺)
为什么说被选中的数据映射到画布上
?
画布的尺寸是有限的,也是是随界面布局的变化,时时相对发生变化。
图形(柱状图,折线图,饼图等等)在画布上的比例(相对尺寸)是一定的,那么一个具体的数值,在展现在图形上时,就需要按照一定的比例进行映射。
以本例为例,
画布大概只有 400*600 px
x 轴,只有3个数据 (0,1,2)
y 轴,上的数据很多,是基于时间戳用随机数计算出来的(例如:1510975537362),科学计数法基本上都在 10e12 这个范围。
显然,不可能直接把数值 转化为像素,因此就需要知道 x 和 y 轴的 最大值 和
最小值 然后把每一个具体的值映射为屏幕中的像素值。
上面 encode 中,x 只选择 data.value 中的第一列,也就是范围 0-2;y 轴选择 data.value 中的第二、三列(即开始时间和结束时间范围)
在数值到画布坐标映射时,调用 api.coord([x, y])
这个方法实现。本例中详见 series-custom.renderItem 的说明
data 的结构
本例中,data 是由 echarts.util.each(categories, function (category, index)
生成的。data 的完整结构如下:
data.push({
name: typeItem.name,
value: [
index,
baseTime,
baseTime += duration,
duration
],
itemStyle: {
normal: {
color: typeItem.color
}
}
series-custom.xAxis (series-custom.yAxis)
xAxis 和 yAxis 的作用是绘制坐标轴。
在转置的时候,坐标轴需要配套转置。
series-custom.renderItem
renderItem 是数据映射过程中的关键,其作用好比 excel 中的数据透视表。不同的是,renderItem 需要把图形是什么样子的,定义清楚。
api.coord([x, y])
在本例中,我们需要对坐标轴进行转置,因此,在一开始的时候,修改了 series-custom.encode 中的映射行为。坐标轴对应的数据发生了修改,因此,在映射具体图形坐标的时候,需要配套修改。
var start = api.coord([categoryIndex, api.value(1)]);
var end = api.coord([categoryIndex, api.value(2)]);
api.size([x, y])
这个方法用来获取一组坐标在画布中的坐标轴上对应的长度。这就好比获取地图比例尺的长度。返回值为一个数组,第一个元素对应x轴,第二个元素对应y轴
本例中,我们想知道转置以后,x轴一个单位的长度。
var width = api.size([1, 1])[0] * 0.6;
源码
var data = [];
var dataCount = 10;
var startTime = +new Date();
var categories = ['categoryA', 'categoryB', 'categoryC'];
var types = [
{name: 'JS Heap', color: '#7b9ce1'},
{name: 'Documents', color: '#bd6d6c'},
{name: 'Nodes', color: '#75d874'},
{name: 'Listeners', color: '#e0bc78'},
{name: 'GPU Memory', color: '#dc77dc'},
{name: 'GPU', color: '#72b362'}
];
// Generate mock data
echarts.util.each(categories, function (category, index) {
var baseTime = startTime;
for (var i = 0; i < dataCount; i++) {
var typeItem = types[Math.round(Math.random() * (types.length - 1))];
var duration = Math.round(Math.random() * 10000);
data.push({
name: typeItem.name,
value: [
index,
baseTime,
baseTime += duration,
duration
],
itemStyle: {
normal: {
color: typeItem.color
}
}
});
baseTime += Math.round(Math.random() * 6000);
}
});
function renderItem(params, api) {
var categoryIndex = api.value(0);
var start = api.coord([categoryIndex, api.value(1)]);
var end = api.coord([categoryIndex, api.value(2)]);
var width = api.size([1, 1])[0] * 0.6;
var height = end[1] - start[1];
var x = start[0] - width / 2;
var y = start[1];
return {
type: 'rect',
shape: {
x: x,
y: y,
width: width,
height: height
},
style: api.style()
};
}
option = {
tooltip: {
formatter: function (params) {
return params.marker + params.name + ': ' + params.value[3] + ' ms';
}
},
title: {
text: 'Profile',
left: 'center'
},
legend: {
data: ['bar', 'error']
},
dataZoom: [{
type: 'slider',
filterMode: 'weakFilter',
showDataShadow: false,
top: 400,
height: 10,
borderColor: 'transparent',
backgroundColor: '#e2e2e2',
handleIcon: 'M10.7,11.9H9.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line
handleSize: 20,
handleStyle: {
shadowBlur: 6,
shadowOffsetX: 1,
shadowOffsetY: 2,
shadowColor: '#aaa'
},
labelFormatter: ''
}, {
type: 'inside',
filterMode: 'weakFilter'
}],
grid: {
height:500
},
yAxis: {
min: startTime,
scale: true,
axisLabel: {
formatter: function (val) {
return Math.max(0, val - startTime) + ' ms';
}
}
},
xAxis: {
data: categories
},
series: [{
type: 'custom',
renderItem: renderItem,
itemStyle: {
normal: {
opacity: 0.8
}
},
encode: {
x: 0,
y: [1, 2]
},
data: data
}]
};