调研了下动态曲线绘制的开源项目,HighCharts应用在AngularJS上相对容易使用和集成,来看下在AngularJS上集成HighCharts的StockChart图表,实现动态绘制Android内存和CPU的变化曲线。
一、实现效果
先看下HightCharts CPU和内存图表显示实现效果,图表左上角可以选择显示的区间范围,1分钟、5分钟或显示全部,截图显示的是全部数据,底部栏可以拖动查看不同区间数据。
二、HighCharts集成
先到HighCharts下载中心下载对应的资源,这边下载的是Highcharts-Stock-8.1.2,Highcharts-Stock下载之后就不用下载Highcharts资源,Highcharts-Stock里面已经包含,也可以直接使用CDN 文件而不用下载:
// Highcharts
<script src="http://cdn.highcharts.com.cn/highcharts/8.1.2/highcharts.js"></script>
// Highcharts Stock
<script src="http://cdn.highcharts.com.cn/highstock/8.1.2/highstock.js"></script>
// Highcharts Maps
<script src="http://cdn.highcharts.com.cn/highmaps/8.1.2/highmaps.js"></script>
// Highcharts Gantt
<script src="http://cdn.highcharts.com.cn/gantt/8.1.2/highcharts-gantt.js"></script>
还可以通过 NPM 安装:
npm install highcharts --save
或者通过 Bower 安装:
bower install highcharts --save
在Highcharts-Stock-8.1.2下载完成之后,把Highcharts-Stock-8.1.2/code/highstock.js文件拷贝放到工程目录下,添加highstock.js引用声明,之后就可以在工程里面使用Highcharts:
script(src='*****/*****/highstock.js')
三、Highcharts-Stock绘制CPU曲线
直接上代码,先看下CPU图表的参数初始化:
前端界面: div(id='cpu')
JS文件,renderTo参数就是指定前端界面展示的控件id :
var chartCpuOptions = { chart: { renderTo: 'cpu' }, xAxis: [{ // currentDateIndicator: true, min: new Date().getTime(), max: new Date().getTime() + 24 * 60 * 60 * 1000 }], rangeSelector: { buttons: [{ count: 1, type: 'minute', text: '1M' }, { count: 5, type: 'minute', text: '5M' }, { type: 'all', text: 'All' }], inputEnabled: false, selected: 0 }, exporting: { enabled: true }, title: { text: 'CPU' }, series: [ { name: 'Process CPU', data: [] }, { name: 'System CPU', data: [] } ] }; var chartCpu = new Highcharts.StockChart(chartCpuOptions);
xAxis图表横坐标轴参数一定要初始化,否则会报如下异常,min和max就是指定横坐标轴的最小值和最大值,xAxis的参数说明可以参考HighCharts API xAxis参数文档:
Error: <rect> attribute x: Expected length, "NaN".
rangeSelector就是图表范围选择器,这边配置1分钟、5分钟和全部显示三个类型,buttons的type参数取值可以是:”millisecond”, “second”, “minute”, “hour”, “day”, “week”, “month”, “ytd”, “all”。
exporting.enabled是否启用导出图表按钮,默认是true,但配置了之后竟然不会显示。
title.text配置图表显示的标题,也可以调整显示的位置。
series配置图表显示的数据列,name参数就是鼠标移动到图表曲线的时候显示的数据列悬浮提示内容。
四、Highcharts-Stock绘制内存曲线
内存曲线图表参数的初始化也类似,前端界面:
div(id='memory')
JS文件:
var chartMemoryOptions = { chart: { renderTo: 'memory' }, xAxis: { // currentDateIndicator: true, min: new Date().getTime(), max: new Date().getTime() + 24 * 60 * 60 * 1000 }, rangeSelector: { buttons: [{ count: 1, type: 'minute', text: '1M' }, { count: 5, type: 'minute', text: '5M' }, { type: 'all', text: 'All' }], inputEnabled: false, selected: 0 }, exporting: { enabled: true }, title: { text: 'Memory' }, series: [ { name: 'Total', data: [] }, { name: 'Native', data: [] }, { name: 'Dalvik', data: [] }, { name: 'Sommap', data: [] } ] }; var chartMemory = new Highcharts.StockChart(chartMemoryOptions);
五、Highcharts图表数据刷新
1. CPU数据的刷新显示
cpulist和memorylist都是JSON结构的数据源:
//每次都设置最新的数据来更新图表 var cpuSize = cpulist.length; var cpuTimeStamp = new Date(parseFloat(cpulist[cpuSize - 1].time)).getTime(); chartCpu.series[0].addPoint([cpuTimeStamp, parseFloat(cpulist[cpuSize - 1].processRate)]); chartCpu.series[1].addPoint([cpuTimeStamp, parseFloat(cpulist[cpuSize - 1].systemRate)]);
//更新一次X轴的最小值和最大值,避免和初始化数据设置不一致导致图表刷新显示问题。 var hasInitCPUxAxis = false; if(!hasInitCPUxAxis){ hasInitCPUxAxis = true; chartCpu.xAxis[0].setExtremes(cpuTimeStamp - 60 * 1000, cpuTimeStamp); }
2. Memory数据的刷新显示
var memSize = memorylist.length; var memTimeStamp = new Date(parseFloat(memorylist[memSize - 1].time)).getTime(); chartMemory.series[0].addPoint([memTimeStamp, parseFloat((parseInt(memorylist[memSize - 1].totalPss) / 1024).toFixed(2))]); chartMemory.series[1].addPoint([memTimeStamp, parseFloat((parseInt(memorylist[memSize - 1].nativePss) / 1024).toFixed(2))]); chartMemory.series[2].addPoint([memTimeStamp, parseFloat((parseInt(memorylist[memSize - 1].dalvikPss) / 1024).toFixed(2))]); chartMemory.series[3].addPoint([memTimeStamp, parseFloat((parseInt(memorylist[memSize - 1].soMmapPss) / 1024).toFixed(2))]); var hasInitMemoryXAxis = false; if(!hasInitMemoryXAxis){ hasInitMemoryXAxis = true; chartMemory.xAxis[0].setExtremes(memTimeStamp - 60 * 1000, memTimeStamp); }
3. 遇到的问题
这边数据更新要注意下,不能用chartCpuOptions.series[0].data.push()的方式,而要用chartMemory.series[0].addPoint([x,y]) 才有效。使用不当会输出如下异常:
Uncaught TypeError: d[w].destroyElements is not a function
at n.generatePoints (highstock.js:5899)
at n.e.generatePoints (highstock.js:9605)
at n.translate (highstock.js:5939)
at n.redraw (highstock.js:6243)
at highstock.js:4731
at Array.forEach (<anonymous>)
at k.Chart.redraw (highstock.js:4730)
at k.Chart.setSize (highstock.js:4937)
at highstock.js:4904
另外注意添加的数据点参数不能是String类型的数据,最好加下parseInt或parseFloat的转化,控制台输出的异常提示如下:
Uncaught Error: Highcharts error #14: www.highcharts.com/errors/14/ at k.Chart.e (highstock.js:98) at k.fireEvent (highstock.js:487) at k.error (highstock.js:110) at n.setData (highstock.js:5796) at n.g.updatedDataHandler (highstock.js:8923) at highstock.js:485 at k.fireEvent (highstock.js:486) at highstock.js:4709 at Array.forEach (<anonymous>) at k.Chart.redraw (highstock.js:4707) at n.addPoint (highstock.js:6658) at Socket.dataReceiveListener (performance-monitor.js:286) at Socket.Emitter.emit (index.js:131) at Socket.onevent (socket.js:263) at Socket.onpacket (socket.js:221) at Manager.eval (index.js:21)
关于error #14的说明如下:
String value sent to series.data, expected Number This happens if using a string as a data point, for example in a setup like this: series: [{ data: ["3", "5", "1", "6"] }] Highcharts expects numerical data values. The most common reason for this error this is that data is parsed from CSV or from a XML source, and the implementer forgot to run parseFloat on the parsed value. Note: For performance reasons internal type casting is not performed, and only the first value is checked (since 2.3).
如果遇到其他问题可以到HighCharts BBS论坛上找答案。
六、参考资料
highcharts + angularjs + live random data example
扩展阅读:
转载请注明出处:陈文管的博客 – AngularJS集成HighCharts动态绘制CPU和内存变化曲线
扫码或搜索:文呓
微信公众号 扫一扫关注