要绘制柱状图, 需要对数据结构和算法有较好的掌握, 能够自由地在各种数据结构中进行转换。通过此例, 是想再次说明了结构与算法在实际开发工作中的应用。

       本文给出使用 highchart 绘制柱状图的通用方法与接口, 只要指定相应的数据结构和配置, 就可以直接拿来使用。

       一、  数据结构与基本接口  

       一般绘制图形, 会涉及到较复杂的数据结构, 比如使用 jsPlumb 绘制拓扑图的通用接口 。方法是, 首先要弄清楚绘制图形所需要的数据结构,然后根据API文档设计一个公共接口,

并写好详细的文档,避免日后忘记。先从最基本的接口开始, 见下面代码。 这是根据静态示例, 将需要动态生成或配置数据的地方抽取出来做成的接口。      

     

/**

* 创建柱状图(基本接口)

* @param chartDivName 用来绘制柱状图的 DIV-ID 值

* @param chartData 柱状图数据结构

* categories: ['c1', 'c2', ..., 'Cn']

* series: [

* { name: 'var1', data: [d11, d12, ..., d1n]},

* { name: 'var2', data: [d21, d22, ..., d2n]},

* ...,

* { name: 'varN', data: [dn1, dn2, ..., dnn]}

* ]

* @param chartConfig 柱状图全局配置

* title: 图表标题

* @returns

*/

function generateColumnChart(chartDivName, chartData, chartConfig) {

var displayFormatter = function() { // 当鼠标悬停图表上时, 格式化提示信息

var tipText = '' + this.x + '';

var total = 0;

$.each(this.points, function(i, point) {

total += point.y;

});

$.each(this.points, function(i, point) {

tipText += '
'+ point.series.name +': '+ Highcharts.numberFormat((point.y*100 / total), 2) + '%' + '(' + point.y + ')';

});

return tipText;

};

var chartObj = obtainCommonChartObj(displayFormatter);

chartObj.title.text = chartConfig.title;

chartObj.xAxis.categories = chartData.categories;

chartObj.series = chartData.series;

var seriesNum = (chartData.series == null ? 0 : chartData.series.length);

for (var k=0; k < seriesNum; k++) {

chartObj.series[k].type = 'column';

}

var chartdiv = $('#'+chartDivName);

chartdiv.highcharts(chartObj);

}

function obtainCommonChartObj(displayFormatterFunc) {

var commonChartObj = {

chart: {

zoomType: 'x',

events: {

click: null

},

resetZoomButton: {

position: {

x: -10,

y: 10

},

relativeTo: 'chart'

}

},

// 去掉 highcharts.com 链接

credits: {

enabled: false,

text: ''

},

plotOptions: {

series: {

// 去掉点的marker, 使图形更美观

marker: {

enabled: false,

states: {

hover: {

enabled: true

}

}

},

turboThreshold: 0,

events: {

click: null

}

},

line: {

lineWidth: 1.5

}

},

series: [],

xAxis: {

},

yAxis: {

title: {

text: ''

},

min: 0

},

tooltip: {

crosshairs: true,

shared: true,

formatter: displayFormatterFunc

},

title: {

// 动态显示图表标题

text: '',

align: 'center',

style: {

fontSize: '12px',

margin: '3px'

}

}

};

return commonChartObj ;

}

       二、 对象数组与结构转化

       通常, 从服务端后台返回的数据结构是对象数组, 要使用基本接口来绘制, 就需要进行数据结构转化。 因此, 在基本接口之上, 可以构建一个高层接口, 见如下代码所示。 如果要转化的数据结构比较复杂, 那么, 可以拿一个静态的输入/输出数据示例作为范本来辅佐思考, 先通过硬编码来实现目标, 然后再将硬编码用可配置项替换掉, 达到可扩展、灵活的目标。   

  

/**

* 创建柱状图(针对对象数组的高层接口)

* @param chartDivName 用来绘制柱状图的 DIV-ID 值

* @param chartData 对象数组

* categories: ['c1', 'c2', ..., 'Cn']

* data:

* [{'field1': 'v11', 'field2': 'v12', ..., 'fieldN': 'v1N'},

* {'field1': 'v21', 'field2': 'v22', ..., 'fieldN': 'v2N'},

* ...,

* {'field1': 'vN1', 'field2': 'vN2', ..., 'fieldN': 'vNN'}]

* @param chartConfig 柱状图全局配置

* title: 图表标题

* categoryField: 分类字段

* groupField: 用于创建 legend 的分组字段

* valueField: 用于显示 Y 轴的字段

* @returns

*/

function generateColumnChartHighLevel(chartDivName, chartData, chartConfig) {

var groupField = chartConfig.groupField;

var valueField = chartConfig.valueField;

var categoryField = chartConfig.categoryField;

var categories = chartData.categories;

var groupedChartData = groupByField(chartData.data, groupField);

var series = [];

for (var i=0; i< groupedChartData.length; i++) {

var groupName = groupedChartData[i][groupField];

var groupData = groupedChartData[i]['data'];

var fieldData = [];

for (var k=0; k < groupData.length; k++) {

// 每个分类的值必须与相应的分类对应, 应对这样的情况

// 对于每个 groupField, 并不是所有 categories 都有值, 可以通过测试例子看出来

// 苹果在 Q3 对应的值是缺失的, 香蕉在 Q2 对应的值是缺失的

var categoryPosition = getCategoryPosition(categories, groupData[k][categoryField]);

if (categoryPosition != -1) {

fieldData[categoryPosition] = groupData[k][valueField];

}

}

for (var j=0; j < categories.length; j++) {

// 缺失值填充

if (fieldData[j] == null) {

fieldData[j] = 0;

}

}

var subseries = {

name: groupName,

data: fieldData,

};

series.push(subseries);

}

var data = {};

data.categories = categories;

data.series = series;

generateColumnChart(chartDivName, data, chartConfig);

}

/**

* 检测 value 在 categories 中的位置

* @param categories

* @param value

*/

function getCategoryPosition(categories, value) {

for (var index=0; index < categories.length; index++) {

if (categories[index] == value) {

return index;

}

}

return -1;

}

/**

* 将指定 chartData 数据按照指定字段的值分类

* eg. [{'timestamp': 'time0', cpu: '0', sys: '15', usr: '20'}, {'timestamp': 'time0', cpu: '1', sys: '16', usr: '21'},

* {'timestamp': 'time1', cpu: '0', sys: '20', usr: '13'}, {'timestamp': 'time1', cpu: '1', sys: '18', usr: '10'}]

* 转换为 [{ cpu:'0', data: [{'timestamp': 'time0', cpu: '0', sys: '15', usr: '20'}, {'timestamp': 'time1', cpu: '0', sys: '20', usr: '13'}] } ,

* { cpu:'1', data: [{'timestamp': 'time0', cpu: '1', sys: '16', usr: '21'}, {'timestamp': 'time1', cpu: '1', sys: '18', usr: '10'}] }]

*/

var groupByField = function(chartDataGathered, fieldName) {

var fieldDataMappingArray = [];

var fieldData = {};

var i=0, num = (chartDataGathered == null ? 0 : chartDataGathered.length);

for (i=0; i

var fieldValue = chartDataGathered[i][fieldName];

fieldData = obtainFieldData(fieldDataMappingArray, fieldName, fieldValue);

if (fieldData == null) {

fieldData = {};

fieldData[fieldName] = fieldValue;

fieldData['data'] = [];

fieldDataMappingArray.push(fieldData);

}

fieldData['data'].push(chartDataGathered[i]);

}

return fieldDataMappingArray;

}

/**

* 在 fieldDataMappingArray 中检测是否有 fieldName = fieldValue 的对象, 若有则返回; 若没有则返回 null

* @param fieldDataMappingArray [{'fieldName': 'fieldValue1', 'data':[]}, {'fieldName': 'fieldValue2', data: []}]

* @param fieldName the name of field

* @param fieldValue the value of field

*/

var obtainFieldData = function(fieldDataMappingArray, fieldName, fieldValue) {

var k=0, dataArrayLength = (fieldDataMappingArray == null ? 0 : fieldDataMappingArray.length);

var fieldData = {};

var existFieldData = {};

if (dataArrayLength == 0) {

return null;

}

for (k=0; k

existFieldData = fieldDataMappingArray[k];

if (existFieldData[fieldName] == fieldValue) {

return existFieldData;

}

}

return null;

}

     

       三、 使用接口     

  

highcharts 绘图示例

        四、 效果图

         

        五、 小结

        要绘制柱状图, 需要对数据结构和算法有较好的掌握, 能够自由地在各种数据结构中进行转换。通过此例, 是想再次说明了结构与算法在实际开发工作中的应用。 当然, 文中给出的代码并非是最优的, 作为一个基本的解法, 里面还是有很多可改进之处。 

 

推荐文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。