Morris.js和flot绘制折线图的比较
【文章摘要】
最近用开源的AdminLTE做框架感觉效果特别好,其针对图表库Morris.js和flot都提供了不错的支持,也都提供了这两者的例子。不过Morris.js是基于Raphael.js来的,也就是其使用SVG和VML来绘制图形,而flot则是使用Canvas进行绘制,在绘制效率和浏览器兼容性等方面会有出入,同时两者需要的数据格式也不相同。本文中对两者的使用和性能进行了比较。
【文章索引】
【一、Morris.js的使用】
Morris.js最新版本是0.5.1,使用BSD协议,可以从官方网站 http://morrisjs.github.io/morris.js/ 或 GitHub https://github.com/morrisjs/morris.js 下载。Morris.js使用非常简单,其仅提供了折线图、面积图、柱形图和饼图四种类型的图表,不过也基本满足大部分需求。Morris.js基于Raphael,使用的是SVG或VML绘制图表,直接支持IE 6+、FF 3+、Chrome 5+、Safari 3+和Opera 9.5+,不过在图表内容比较多时效率比较低,这个之后再分析。除此之外Morris.js还需要依赖jQuery。
其数据格式要求如下:
bathroomData = [
{ time: "2014-06-17T10:54:01", r2: 5 },
{ time: "2014-06-17T11:09:01", r0: 4, r1: 79, r2: 7, r3: 79 },
{ time: "2014-06-17T11:24:01", r0: 11, r1: 88, r2: 13, r3: 100 },
{ time: "2014-06-17T11:39:01", r0: 11, r1: 69, r2: 12, r3: 73 },
...
]
即按X轴对数据进行分类,X轴数据(如上的time)相同的所有系列(Series)的数据(如上的r0、r1、r2、r3)均放置于在同一个JS对象内,当该系列在该X轴数据上没有数据时留空(如上第一个数据没有r0、r1、r3)。之后需要指定X轴和Y轴的数据项,X轴只能指定一个数据项,而Y轴可以指定多个。除此之外,Morris.js自带日期型字符串的支持,直接使用即可。例如这里指定time为X轴的数据,r0、r1、r2、r3为Y轴数据,默认效果如下:
其中完整代码如下:
<!DOCTYPE html>
<html lang="zh-cn">
<body>
<div id="bathroom-chart"></div> <script type="text/javascript" src="jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="raphael-2.1.2.min.js"></script>
<script type="text/javascript" src="morris-0.5.1.min.js"></script>
<script type="text/javascript">
var bathroomData = [
{ time: "2014-06-17T10:54:01", r2: 5 },
{ time: "2014-06-17T11:09:01", r0: 4, r1: 79, r2: 7, r3: 79 },
{ time: "2014-06-17T11:24:01", r0: 11, r1: 88, r2: 13, r3: 100 },
{ time: "2014-06-17T11:39:01", r0: 11, r1: 69, r2: 12, r3: 73 }
];
var bathroomIDs = [ "r0", "r1", "r2", "r3" ];
var bathroomNames = [ "校本部学生浴室(男)", "校本部学生浴室(女)", "XX校区学生浴室(男)", "XX校区学生浴室(女)" ]; Morris.Line({
element: "bathroom-chart",
data: bathroomData,
xkey: "time",
ykeys: bathroomIDs,
labels: bathroomNames
});
</script>
</body>
</html>
其中element为绘制到指定元素的元素ID,data为数据源,xKey为X轴的数据项名称,yKeys为Y轴的数据项名称数组,labels为每个系列的名称数组,与yKeys的顺序对应。这样就可以实现基本的图表,而且其自带鼠标悬停的效果,即鼠标在图上悬停时,图表下方会有各个系列当前选中的值。
除此之外还可以对每个系列的颜色进行设置(lineColors属性设置数组,与yKeys顺序对应)、线条粗细(lineWidth)、圆圈大小(pointSize)、Y轴最大值、最小值(ymax、ymin)、X轴数据间隔类型(xLabels)等等进行设置。此外还可以通过自定义函数来自定义X轴和Y轴的数据格式,或者自定义鼠标悬停时显示的内容。
稍加修改可以调整为更好看的效果,如下所示:
完整代码如下:
<!DOCTYPE html>
<html lang="zh-cn">
<body>
<div id="bathroom-chart"></div> <script type="text/javascript" src="jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="raphael-2.1.2.min.js"></script>
<script type="text/javascript" src="morris-0.5.1.min.js"></script>
<script type="text/javascript">
var bathroomData = [
{ time: "2014-06-17T10:54:01", r2: 5 },
{ time: "2014-06-17T11:09:01", r0: 4, r1: 79, r2: 7, r3: 79 },
{ time: "2014-06-17T11:24:01", r0: 11, r1: 88, r2: 13, r3: 100 },
{ time: "2014-06-17T11:39:01", r0: 11, r1: 69, r2: 12, r3: 73 }
];
var bathroomIDs = [ "r0", "r1", "r2", "r3" ];
var bathroomNames = [ "校本部学生浴室(男)", "校本部学生浴室(女)", "XX校区学生浴室(男)", "XX校区学生浴室(女)" ];
var bathroomMaxs = [ 100, 100, 113, 137 ];
var bathroomMax = 137;
var bathroomColors = [ "#5B9BD5", "#ED7D31", "#A5A5A5", "#FFC000" ]; Morris.Line({
element: "bathroom-chart",
data: bathroomData,
xkey: "time",
ykeys: bathroomIDs,
labels: bathroomNames,
lineColors: bathroomColors,
lineWidth: 3,
pointSize: 4,
ymax: bathroomMax,
ymin: 0,
hoverCallback: function (index, options, content) {
var row = options.data[index];
var result = '<div class="morris-hover-row-label">' + row.time.replace("T", " ") + '</div>'; for (var i = 0; i < bathroomNames.length; i++) {
result += '<div class="morris-hover-point" style="color: ' + bathroomColors[i] + '">' +
bathroomNames[i] + " : " + (row["r" + i] == undefined ? "-" : row["r" + i]) + " / " + bathroomMaxs[i] + "</div>";
} return result;
},
xLabels: "5min",
yLabelFormat: function (y) { return parseInt(y).toString(); }
});
</script>
</body>
</html>
【二、flot的使用】
flot也是非常常见的图表库,最新版本是0.8.3,采用MIT协议,可以从官方网站 http://www.flotcharts.org/ 或 GitHub https://github.com/flot/flot 下载。flot使用也很简单,其支持在一个图表中同时叠加折线图、面积图、柱形图/条形图等不同的形式,而且其支持扩展插件,通过扩展插件还可以支持堆积柱形图和饼图等以及支持缩放和选区等功能。flot使用Canvas绘制图表,在借助excanvas的情况下,可以支持IE 6+、FF 2+、Chrome、Safari 3+和Opera 9.5+。
还是以上面的数据为例,flot的数据要求与Morris.js不同,对于上述的数据,flot要求如下:
bathroomData = [
{ data: [ [1402974541000, 4], [1402975441000, 11], [1402976341000, 11], ... ] },
{ data: [ [1402974541000, 79], [1402975441000, 88], [1402976341000, 69], ... ] },
{ data: [ [1402973641000, 5], [1402974541000, 7], [1402975441000, 13], [1402976341000, 12], ... ] },
{ data: [ [1402974541000, 79], [1402975441000, 100], [1402976341000, 73], ... ] }
]
可以看到,与Morris.js不同主要有两点,第一点是flot不支持日期型字符串的使用,需要转换为JavaScript的Date对象,然后使用getTime获取时间(距1970年1月1日 UTC时区的毫秒数)才可。第二点是Morris以X轴对数据进行分类,而flot则以系列(Series)对数据进行分类,每一个系列为一个JS对象,包括一个名为data的数组,其中该数组包含该系列的所有数据,每一个数组又使用一个二维数组表示,其中第一维表示X轴的数据,第二维为Y轴的数据。默认效果如下:
完整代码如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<style>
.chart-container {
height: 330px;
}
.bathroom-chart {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="chart-container">
<div id="bathroom-chart" class="bathroom-chart"></div>
</div> <script type="text/javascript" src="jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="jquery.flot-0.8.3.min.js"></script>
<script type="text/javascript" src="jquery.flot-0.8.3.time.min.js"></script>
<script type="text/javascript">
var bathroomData = [
{ data: [ [1402974541000, 4], [1402975441000, 11], [1402976341000, 11] ], label: "校本部学生浴室(男)" },
{ data: [ [1402974541000, 79], [1402975441000, 88], [1402976341000, 69] ], label: "校本部学生浴室(女)" },
{ data: [ [1402973641000, 5], [1402974541000, 7], [1402975441000, 13], [1402976341000, 12] ], label: "XX校区学生浴室(男)" },
{ data: [ [1402974541000, 79], [1402975441000, 100], [1402976341000, 73] ], label: "XX校区学生浴室(女)" }
]; $.plot("#bathroom-chart", bathroomData, {
xaxis: { mode: "time", timezone: "browser" }
});
</script>
</body>
</html>
其中数据里每个系列还支持设置label参数,即设置这个系列的名称。与Morris.js不同的是,对于日期类型需要手动设置轴的类型,例如这里需要设置X轴的类型为time(设置为time类型时需要添加jquery.flot.time.js这个插件),除了设置类型为时间外,还需要设置时区,如果服务器、客户端的时区均为同一个,那么可以将时区设置为“browser”(与浏览器相同)。
除此之外,flot默认显示图表的边框,可以通过grid参数里的borderWidth为0取消;flot默认的图表margin也过小,可以设置grid的marigin和labelMargin;还可以在series参数里对lines(折线)、points(点)以及bars(柱形)进行设置,每一项均可同时显示或隐藏,具体设置方法可以参考文档,不再赘述。此外,Morris.js的图例默认都在图表内,可以设置legend的container来将图例设置在其他的地方。另外,由于flot没有默认的鼠标悬停效果,所以需要自己手动去定义,flot提供了一个叫“plothover”和“plotclick”的事件,可以绑定这两个事件实现悬停和点击的效果,其中事件处理函数包括三个参数,分别是event、pos以及item,可以通过item.seriesIndex获取系列的索引,或者直接通过item.series.label直接获取系列的名称,或者通过item.datapoint[0]或[1]获取当前X轴或Y轴的数据等等。最终实现效果如下:
完整代码如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<style>
.chart-container {
height: 330px;
}
.chart-tooltip {
position: absolute;
display: none;
padding: 5px;
max-width: 200px;
font-size: 12px;
text-align: center;
color: #fff;
background-color: #000;
border-radius: 4px;
opacity: 0.8;
}
.bathroom-chart {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div class="chart-container">
<div id="bathroom-chart" class="bathroom-chart"></div>
</div>
<div id="bathroom-chart-tooltip" class="chart-tooltip"></div>
<div id="bathroom-legend"></div> <script type="text/javascript" src="jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="jquery.flot-0.8.3.min.js"></script>
<script type="text/javascript" src="jquery.flot-0.8.3.time.min.js"></script>
<script type="text/javascript">
var bathroomData = [
{ data: [ [1402974541000, 4], [1402975441000, 11], [1402976341000, 11] ], label: "校本部学生浴室(男)" },
{ data: [ [1402974541000, 79], [1402975441000, 88], [1402976341000, 69] ], label: "校本部学生浴室(女)" },
{ data: [ [1402973641000, 5], [1402974541000, 7], [1402975441000, 13], [1402976341000, 12] ], label: "XX校区学生浴室(男)" },
{ data: [ [1402974541000, 79], [1402975441000, 100], [1402976341000, 73] ], label: "XX校区学生浴室(女)" }
];
var bathroomMaxs = [ 100, 100, 113, 137 ];
var bathroomMax = 137;
var bathroomColors = [ "#5B9BD5", "#ED7D31", "#A5A5A5", "#FFC000" ]; $.plot("#bathroom-chart", bathroomData, {
xaxis: { mode: "time", timezone: "browser", timeformat: "%H:%S", tickLength: 0 },
yaxis: { min: 0, max: bathroomMax, color: "#EAEAEA" },
legend: { container: "#bathroom-legend" },
colors: bathroomColors,
series: {
lines: { show: true, lineWidth: 3, fillColor: bathroomColors },
points: { show: true } },
grid: { hoverable: true, borderWidth: 0, margin: 10, labelMargin: 10 }
}); $("#bathroom-chart").bind("plothover", function(event, pos, item) {
if (item) {
$("#bathroom-chart-tooltip").html(item.series.label + " : " + item.datapoint[1] + " / " + bathroomMaxs[item.seriesIndex])
.css({ top: item.pageY + 8, left: item.pageX + 8 })
.fadeIn(200);
} else {
$("#bathroom-chart-tooltip").hide();
}
});
</script>
</body>
</html>
【三、性能比较】
性能测试当然不是用上述的这么稀少的数据了,实际的数据是从上午11点到晚上11点这12个小时每5分钟一次的数据,显示出现大约如下图,其实也没多少。
性能测试采用在网页开始时记录当前时间,所有代码均执行完后求当前时间与开始时间的差,其中测试代码均使用上述提到的完整代码(即一种默认样式、一种自定义样式),测试平台选用我手头的几个设备Lenovo Thinkpad T420(i5-2520 2.5Ghz/8GB/Win8.1 x64)、一台服务器虚拟机(E5-4650 2.7Ghz/4GB/Server 2003 x64)、Dell Venue 8 Pro(Atom 3740D 1.33Ghz/2GB/Win8.1 x86)、小米1青春版(Qualcomm MSM8260 1.2Ghz/768MB/MIUI on Android 4.1.2)、小米2S(Snapdragon 600 1.7Ghz/2GB/MIUI on Android 4.1.1),分辨率都是设备默认的全屏分辨率,其中服务器上使用IE8,使用excanvas实现Canvas效果。测试结果如下:
看来SVG相对Canvas还真不是一般的慢呢。
附,原始测试结果:
Morris 0.5.1 基本样式 | |||||||
---|---|---|---|---|---|---|---|
设备类型 | 浏览器 | 第一次 | 第二次 | 第三次 | 第四次 | 第五次 | 平均 |
Thinkpad T420 | IE11 桌面版 | 656 | 621 | 744 | 608 | 607 | 647.2 |
Thinkpad T420 | 搜狗浏览器(Webkit 537.36) | 361 | 339 | 334 | 320 | 332 | 337.2 |
服务器虚拟机 | IE8 | 2343 | 2421 | 2375 | 2391 | 2390 | 2384 |
Dell Venue 8 Pro | IE11 Metro版 | 1966 | 2151 | 2057 | 1978 | 2117 | 2053.8 |
小米1青春版 | UC 9.7国际版(Webkit 533.1) | 4407 | 4335 | 4233 | 4309 | 4442 | 4345.2 |
小米2S | UC 9.8(Webkit 533.1) | 2892 | 2536 | 2481 | 2509 | 2558 | 2595.2 |
Morris 0.5.1 自定义样式 | |||||||
---|---|---|---|---|---|---|---|
设备类型 | 浏览器 | 第一次 | 第二次 | 第三次 | 第四次 | 第五次 | 平均 |
Thinkpad T420 | IE11 桌面版 | 1196 | 1139 | 1206 | 1113 | 1189 | 1168.6 |
Thinkpad T420 | 搜狗浏览器(Webkit 537.36) | 602 | 585 | 631 | 632 | 620 | 614 |
服务器虚拟机 | IE8 | 4468 | 4735 | 4797 | 4750 | 4703 | 4690.6 |
Dell Venue 8 Pro | IE11 Metro版 | 3897 | 4039 | 4207 | 4165 | 4380 | 4137.6 |
小米1青春版 | UC 9.7国际版(Webkit 533.1) | 8246 | 8472 | 8765 | 9003 | 8517 | 8600.6 |
小米2S | UC 9.8(Webkit 533.1) | 5219 | 4920 | 5674 | 4727 | 5152 | 5138.4 |
Flot 0.8.1 基本样式 | |||||||
---|---|---|---|---|---|---|---|
设备类型 | 浏览器 | 第一次 | 第二次 | 第三次 | 第四次 | 第五次 | 平均 |
Thinkpad T420 | IE11 桌面版 | 67 | 52 | 48 | 52 | 50 | 53.8 |
Thinkpad T420 | 搜狗浏览器(Webkit 537.36) | 63 | 34 | 32 | 38 | 42 | 41.8 |
服务器虚拟机 | IE8 | 94 | 110 | 94 | 109 | 109 | 103.2 |
Dell Venue 8 Pro | IE11 Metro版 | 131 | 147 | 146 | 168 | 159 | 150.2 |
小米1青春版 | UC 9.7国际版(Webkit 533.1) | 351 | 471 | 379 | 393 | 409 | 400.6 |
小米2S | UC 9.8(Webkit 533.1) | 349 | 260 | 201 | 221 | 195 | 245.2 |
Flot 0.8.1 自定义样式 | |||||||
---|---|---|---|---|---|---|---|
设备类型 | 浏览器 | 第一次 | 第二次 | 第三次 | 第四次 | 第五次 | 平均 |
Thinkpad T420 | IE11 桌面版 | 87 | 89 | 87 | 78 | 83 | 84.8 |
Thinkpad T420 | 搜狗浏览器(Webkit 537.36) | 40 | 38 | 40 | 44 | 44 | 41.2 |
服务器虚拟机 | IE8 | 1234 | 1219 | 1282 | 1234 | 1235 | 1240.8 |
Dell Venue 8 Pro | IE11 Metro版 | 256 | 290 | 269 | 352 | 312 | 295.8 |
小米1青春版 | UC 9.7国际版(Webkit 533.1) | 719 | 600 | 775 | 749 | 622 | 693 |
小米2S | UC 9.8(Webkit 533.1) | 357 | 273 | 281 | 335 | 340 | 317.2 |
【相关链接】
- morris.js:http://morrisjs.github.io/morris.js/
- Flot:http://www.flotcharts.org/
- Flot API:https://github.com/flot/flot/blob/master/API.md
Morris.js和flot绘制折线图的比较的更多相关文章
- 用canvas绘制折线图
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- php中用GD绘制折线图
php中用GD绘制折线图,代码如下: Class Chart{ private $image; // 定义图像 private $title; // 定义标题 private $ydata; // 定 ...
- UUChart的使用--iOS绘制折线图
UUChart是一个用于绘制图表的第三方,尤其适合去绘制折线图. 二.下载地址: https://github.com/ZhipingYang/UUChartView 三.使用 第一步.首先我们将下载 ...
- html5绘制折线图
html5绘制折线图详细代码 <html> <canvas id="a_canvas" width="1000" height="7 ...
- Android自己定义组件系列【9】——Canvas绘制折线图
有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了非常多插件,可是非常多时候我们须要依据详细项目自己定义这些图表,这一篇文章我们一起来看看怎样在Android中使用Can ...
- JFreeChart绘制折线图实例
JFreeChart是JAVA平台上的一个开放的第三方图表绘制类库.只要下载JFreeChart的类库,导入项目即可使用.下面是一个绘制折线图的实例.各处注释都已经写的比较清晰了. package c ...
- 【带着canvas去流浪】(2)绘制折线图
目录 一. 任务说明 二. 重点提示 三. 示例代码 3.1 一般折线图 3.2 用贝塞尔曲线绘制平滑折线图 四. 大数据量场景 示例代码托管在:https://github.com/dashnowo ...
- python使用matplotlib绘制折线图教程
Matplotlib是一个Python工具箱,用于科学计算的数据可视化.借助它,Python可以绘制如Matlab和Octave多种多样的数据图形.下面这篇文章主要介绍了python使用matplot ...
- 用PNChart绘制折线图
写在前面 上一篇文章已经介绍过用PNChart绘制饼状图了,绘制折线图的步骤和饼状图的步骤是相似的,按照中的准备做好准备工作后就可以绘制折线图了. 开始使用 1.在view中声明一个PNLineCha ...
随机推荐
- HEAP CORRUPTION DETECTED :after Normal block 错误
http://blog.csdn.net/zhccl/article/details/7889590
- nodejs学习
转自于网络: ubuntu 下面安装 vim 的问题 1.输入vim时,显示: 程序"vim"已包含在以下软件包中: * vim * vim-gnome * vim-tiny * ...
- yii2的分页和ajax分页
要想使用Yii分页类第一步:在控制器层加载分页类 use yii\data\Pagination;第二步: 使用model层查询数据,并用分分页,限制每页的显示条数$data = User::find ...
- 前端弹出层框架layer
http://www.layui.com/doc/modules/layer.html#layer.confirm
- Makefile笔记之一 ------ 变量的引用及赋值
1.变量的引用方式: "$(变量名)"或者"¥{变量名}" 例如: ${Objs}就是取变量Objs的值 注意: 当变量名为单字符是可以采用:"$a& ...
- 多线程之信号量(By C++)
信号量在多线程中,主要是用于线程的同步或者限制线程运行的数量. 所谓同步,当流程1运行在线程1中,流程2运行在线程2中,流程2必须在流程1结束之后才能开始执行.你会怎么做,所有就需要给出一个流程1结束 ...
- BZOJ2342 Manacher + set
题一:别人介绍的一道题,题意是给出一个序列,我们要求出一段最常的连续子序列,满足:该子序列能够被平分为三段,第一段和第二段形成回文串,第二段和第三段形成回文串. 题二:BZOJ2342和这题非常的相似 ...
- ubuntu server 搭建自己的个人博客及其他网站
一, 安装apache2服务器 sudo apt-get install apache2 二,安装mysql服务器 sudo apt-get install mysql-server 此时会提示输入M ...
- CentOS 7 学习笔记(一)时间管理
1 获取当前时间 [root@limt01 ~]# date 2015年 05月 22日 星期五 01:30:50 CST 2 获取当前日期 [root@limt01 ~]# date "+ ...
- C到C++的升级
const 在C中只是个“只读变量”,并不是真正意义上的常量,通过指针能改变它,如下 #include<stdio.h> int main() { ;//声明只读变量a为0 int* p= ...