使用 amcharts 和 highcharts 绘制多曲线图的通用方法
工作中用到, 这里分享一下。 可以使用 amcharts 和 highcharts 在同一坐标中绘制多个对比曲线图。 当然, 对图形没有过多装饰, 可以参考 API 文档:
highcharts: Highcharts API
amcharts: amcharts API
0. 说明
amcharts 与 highcharts 对于数据格式的要求是不一样的。 amcharts 只需要一个 对象数组 [{'x': 1, 'y': 2, 'z': 3}, {'x':2, 'y': 4, 'z': 6}], 并指明 x ,y 轴的字段名,其它的就交给 amcharts 了; 而 highcharts 则需要对每个曲线图定义好二维数组 , [[1,2], [2,4]] , [[1,3], [2,6]] ; 如果要使用对象数组返回格式, 就需要进行数据抽取和重组, 以符合 highchart 的要求。
由于 javascript 所使用的标准数据格式是 JSON, 因此, 可以非常方便地进行数据拼接和组合。 也就是说, 如果要在同一坐标中绘制多个曲线图, 只需要定义一个数组, 将多个曲线图的数据加入数组即可。
实现中, 有三点需要重点说明:
a. 时间字段。 由于不同的API 返回的JSON数据中,时间的字段名不一定相同(比如有的为 time, 有的为 timestamp), 并且时间的格式有所不同(比如有的为字符串, 有的为时间戳), 为了追求灵活性, 需要使用一个日期转换函数 convertDate(chartData, timeStampFieldName, dateConvertFunc) 来统一处理。 这里统一转换为 javascript timestamp 再转化为 Date 类型, 可以兼容 Firefox 和 Chrome .
b. 返回数据的格式, 主要以对象数组为主;
c. 由于要绘制任意多个曲线图, 这里也需要考虑到灵活性, 利用了 JSON 格式的灵活性, 采用循环方式加入多个曲线的配置项来实现。
1. 返回数据格式
{"result":{"msg":null,"code":200,"data":[{"__source__":"10.201.20.35","__time__":"1380446527","blocked":"0","plist":"1517","runq":"0","ldavg_1":"2.34","ldavg_5":"1.56","ldavg_15":"1.43","time":"Sun Sep 29 17:22:07 2013"},{"__source__":"10.201.20.35","__time__":"1380446587","blocked":"0","plist":"1524","runq":"5","ldavg_1":"2.38","ldavg_5":"1.69","ldavg_15":"1.48","time":"Sun Sep 29 17:23:07 2013"},{"__source__":"10.201.20.35","__time__":"1380446647","blocked":"0","plist":"1523","runq":"3","ldavg_1":"1.51","ldavg_5":"1.56","ldavg_15":"1.45","time":"Sun Sep 29 17:24:07 2013"}],"success":true}}
2. HTML DIV
<body>
<div id="perfcharts">
<div id="Loadperfchartdiv" style="width:100%; height:400px;" class="chartdiv"></div>
</div>
</body>
3. 使用 amcharts 绘制:
需要导入
<script src="../amcharts.js" type="text/javascript"></script>
<script src="../jquery-1.10.1.min.js" type="text/javascript"></script>
<script src="../drawchart.js" type="text/javascript"></script>
drawchart.js 是编写的基于 amcharts 的在同一坐标中绘制多图形的客户端接口:
/**
* 使用 amcharts 绘制时间趋势曲线图
*
* generateChart:
* chartDiv 绘图所需要的 DIV 区域名称;
* chartData 绘图所需要的数据
* chartConfig 绘图的全局配置对象
* lineConfigArray 每个曲线图的配置对象(配置Y轴)
*
*/
var globalChart = null, globalChartData = null;
function generateChart(chartDiv, chartData, chartConfig, lineConfigArray) {
console.log('begin draw chart: ' + getNow());
// SERIAL CHART
var chart = new AmCharts.AmSerialChart();
globalChart = chart;
globalChartData = chartData;
chart.pathToImages = "../resources/images/amcharts2/";
chart.zoomOutButton = {
backgroundColor: '#000000',
backgroundAlpha: 0.15
};
chart.dataProvider = chartData;
chart.categoryField = "timeStamp";
// data updated event will be fired when chart is first displayed,
// also when data will be updated. We'll use it to set some
// initial zoom
chart.addListener("dataUpdated", zoomChart);
// AXES
// Category
var categoryAxis = chart.categoryAxis;
categoryAxis.parseDates = true; // in order char to understand dates, we should set parseDates to true
categoryAxis.minPeriod = "mm"; // as we have data with minute interval, we have to set "mm" here.
categoryAxis.gridAlpha = 0.07;
categoryAxis.axisColor = "#DADADA";
categoryAxis.labelFunction = function(valueText, date, categoryAxis) {
var MM = date.getMonth()+1;
var dd = date.getDate();
var hh = date.getHours();
if(hh<10) hh = '0' + hh;
var mm = date.getMinutes();
if(mm<10) mm = '0' + mm;
var ss = date.getSeconds();
return MM+'-'+dd+' '+hh+':'+mm;
}
// Value
var valueAxis = new AmCharts.ValueAxis();
valueAxis.gridAlpha = 0.07;
valueAxis.title = chartConfig.title;
chart.addValueAxis(valueAxis);
// GRAPH
for (var i=0; i<lineConfigArray.length;i++) {
var graph = new AmCharts.AmGraph();
graph.type = "line";
graph.title = lineConfigArray[i].title;
graph.valueField = lineConfigArray[i].valueField;
graph.lineAlpha = 1;
graph.lineColor = lineConfigArray[i].lineColor;
chart.addGraph(graph);
}
// CURSOR
var chartCursor = new AmCharts.ChartCursor();
chartCursor.cursorPosition = "mouse";
chartCursor.categoryBalloonDateFormat = "MM DD JJ:NN";
chart.addChartCursor(chartCursor);
// SCROLLBAR
var chartScrollbar = new AmCharts.ChartScrollbar();
chart.addChartScrollbar(chartScrollbar);
// LEGEND
var legend = new AmCharts.AmLegend();
legend.marginLeft = 110;
chart.addLegend(legend);
// WRITE
chart.write(chartDiv);
console.log('end draw chart: ' + getNow());
}
function convertDate(chartData, timeStampFieldName, dateConvertFunc) {
for (var i=0; i<chartData.length;i++) {
var timeStamp_i = chartData[i][timeStampFieldName == null ? "timeStamp": timeStampFieldName];
if (typeof dateConvertFunc === 'function') {
chartData[i].timeStamp = dateConvertFunc(timeStamp_i);
}
else {
chartData[i].timeStamp = new Date(timeStamp_i);
}
}
return chartData;
}
function zoomChart() {
globalChart.zoomToIndexes(0, globalChartData.length - 1);
}
客户端使用方法 :
AmCharts.ready(function () {
var drawLoadperf = function() {
$.ajax({
dataType: "json",
url: httpPrefix + '/controllers/sls/obtainNcLoad',
data: 'Category=load_log_index&' + params,
success: function(data) {
var chartData = convertDate(data.result.data, "time");
generateChart('Loadperfchartdiv', chartData,
{'title': 'Load'},
[{'title':'load_1', 'valueField': 'ldavg_1', 'lineColor': '#ff0000'},
{'title':'load_5', 'valueField': 'ldavg_5', 'lineColor': '#00ff00'},
{'title':'load_15', 'valueField': 'ldavg_15', 'lineColor': '#0000ff'}]);
}
});
}
}
amcharts 效果图:
4. 使用 highcharts 绘制:
需要导入:
<script src="../jquery-1.10.1.min.js" type="text/javascript"></script>
<script src="../highcharts.js" type="text/javascript"></script>
<script src="../drawchart_highcharts.js" type="text/javascript"></script>
drawchart_highcharts.js 是基于 highcharts 的在同一坐标中绘制多个图形的客户端接口。
/**
* 使用 highcharts 绘制时间趋势曲线图
*
* generateChart:
* chartDiv 绘图所需要的 DIV 区域名称;
* chartData 绘图所需要的数据
* chartConfig 绘图的全局配置对象
* lineConfigArray 每个曲线图的配置对象(配置Y轴)
*
*/
function generateChart(chartDiv, chartData, chartConfig, lineConfigArray) {
var chartObj = {
chart: {
zoomType: 'x'
},
plotOptions: {
series: {
marker: {
enabled: false,
states: {
hover: {
enabled: true
}
}
}
}
},
series: [],
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
hour: '%b-%e %H:%M'
}
},
yAxis: {
title: {
text: ''
}
},
tooltip: {//当鼠标悬置数据点时的提示框
formatter: function() { //格式化提示信息
return Highcharts.dateFormat('%H:%M', this.x) +'<br/>'+ this.y;
}
},
title: {
text: null
}
};
for (var i=0; i<lineConfigArray.length;i++) {
var subseries = {
name: lineConfigArray[i].title,
data: extract(chartData, lineConfigArray[i].valueField),
color: lineConfigArray[i].lineColor
};
chartObj.yAxis.title.text = chartConfig.title;
chartObj.series.push(subseries);
}
$("#"+chartDiv).highcharts(chartObj);
}
function convertDate(chartData, timeStampFieldName, dateConvertFunc) {
for (var i=0; i<chartData.length;i++) {
var timeStamp_i = chartData[i][timeStampFieldName == null ? "timeStamp": timeStampFieldName];
if (typeof dateConvertFunc === 'function') {
chartData[i].timeStamp = dateConvertFunc(timeStamp_i);
}
else {
chartData[i].timeStamp = new Date(timeStamp_i);
}
}
return chartData;
}
/**
* 从 chartData 中抽取出 valueField 数据
*/
function extract(chartData, valueField) {
var valueData = [];
var i=0, len = chartData.length;
for (i=0; i<len; i++) {
valueData.push([chartData[i].timeStamp, parseFloat(chartData[i][valueField])]);
}
return valueData;
}
客户端使用:
$(function () {
var drawLoadperf = function() {
$.ajax({
dataType: "json",
url: httpPrefix + '/controllers/sls/obtainNcLoad',
data: 'Category=load_log_index&' + params,
success: function(data) {
var chartData = convertDate(data.result.data, "time");
generateChart('Loadperfchartdiv', chartData,
{'title': 'Load'},
[{'title':'load_1', 'valueField': 'ldavg_1', 'lineColor': '#ff0000'},
{'title':'load_5', 'valueField': 'ldavg_5', 'lineColor': '#00ff00'},
{'title':'load_15', 'valueField': 'ldavg_15', 'lineColor': '#0000ff'}]);
}
});
}
}
highcharts 效果图