如何画Flot折线图和柱形图

看完了前面那么多章节,相信你对画折线图和画柱形图都已经有相当的认识了,所以这章就要更进一步的教你如何把折线图和柱形图画在同一张图表上,如果你前面的章节都还没看过,建议你先去看一下如何画折线图以及如何画柱形图再回头来阅读此章.

折线图和柱形图范例

绘图用到的档案

制综合图(折线和柱形图)时,所有需要的档案清单如下.
 
<script type="text/javascript" src="/js/jquery-1.8.3.min.js"></script>      
<!--[if lte IE 8]><script language="javascript" type="text/javascript" src="/js/flot/excanvas.min.js"></script><![endif]-->   
<script type="text/javascript" src="/js/flot/jquery.flot.js"></script>
<script type="text/javascript" src="/js/flot/jquery.flot.time.js"></script>
<script type="text/javascript" src="/js/flot/jquery.flot.axislabels.js"></script>
<script type="text/javascript" src="/js/flot/jquery.flot.symbol.js"></script>
        

建立数据

这个范例用了气象数据当数据,有温度、风速及大气压力.一开始我们先建立3个数组变量分别代表为温度、风速及大气压力,接着再插入数据,x轴我们用的是时间格式数据,我们建立了一个自定义函式gd(),用来取得timestamps,而y轴就是气象数据数据.
 
//temp
var data1 = [
    [gd(2012, 1, 1), 1], [gd(2012, 1, 2), -2], [gd(2012, 1, 3), -2], [gd(2012, 1, 4), 1], [gd(2012, 1, 5), 3], [gd(2012, 1, 6), 4], [gd(2012, 1, 7), 5], [gd(2012, 1, 8), 6], [gd(2012, 1, 9), 7], [gd(2012, 1, 10), 7], [gd(2012, 1, 11), 6], [gd(2012, 1, 12), 7], [gd(2012, 1, 13), 8], [gd(2012, 1, 14), 8], [gd(2012, 1, 15), 3], [gd(2012, 1, 16), 2], [gd(2012, 1, 17), 4], [gd(2012, 1, 18), -1], [gd(2012, 1, 19), 5], [gd(2012, 1, 20), 6], [gd(2012, 1, 21), -2], [gd(2012, 1, 22), -7], [gd(2012, 1, 23), -9], [gd(2012, 1, 24), -8], [gd(2012, 1, 25), -7], [gd(2012, 1, 26), -6], [gd(2012, 1, 27), -3], [gd(2012, 1, 28), 1], [gd(2012, 1, 29), 6], [gd(2012, 1, 30), 9], [gd(2012, 1, 31), 8]
];

//wind
var data2 = [
    [gd(2012, 1, 1), 11], [gd(2012, 1, 2), 9], [gd(2012, 1, 3), 7], [gd(2012, 1, 4), 13], [gd(2012, 1, 5), 11], [gd(2012, 1, 6), 11], [gd(2012, 1, 7), 9], [gd(2012, 1, 8), 10], [gd(2012, 1, 9), 7], [gd(2012, 1, 10), 11], [gd(2012, 1, 11), 7], [gd(2012, 1, 12), 6], [gd(2012, 1, 13), 4], [gd(2012, 1, 14), 5], [gd(2012, 1, 15), 11], [gd(2012, 1, 16), 8], [gd(2012, 1, 17), 9], [gd(2012, 1, 18), 16], [gd(2012, 1, 19), 11], [gd(2012, 1, 20), 18], [gd(2012, 1, 21), 8], [gd(2012, 1, 22), 17], [gd(2012, 1, 23), 11], [gd(2012, 1, 24), 13], [gd(2012, 1, 25), 11], [gd(2012, 1, 26), 11], [gd(2012, 1, 27), 9], [gd(2012, 1, 28), 8], [gd(2012, 1, 29), 7], [gd(2012, 1, 30), 8], [gd(2012, 1, 31), 20]
];

//sea level pressure
var data3 = [
    [gd(2012, 1, 1), 1012], [gd(2012, 1, 2), 1018], [gd(2012, 1, 3), 1020], [gd(2012, 1, 4), 1016], [gd(2012, 1, 5), 1022], [gd(2012, 1, 6), 1023], [gd(2012, 1, 7), 1029], [gd(2012, 1, 8), 1030], [gd(2012, 1, 9), 1029], [gd(2012, 1, 10), 1034], [gd(2012, 1, 11), 1034], [gd(2012, 1, 12), 1023], [gd(2012, 1, 13), 1022], [gd(2012, 1, 14), 1026], [gd(2012, 1, 15), 1027], [gd(2012, 1, 16), 1023], [gd(2012, 1, 17), 1019], [gd(2012, 1, 18), 1032], [gd(2012, 1, 19), 1029], [gd(2012, 1, 20), 1017], [gd(2012, 1, 21), 1015], [gd(2012, 1, 22), 1017], [gd(2012, 1, 23), 1023], [gd(2012, 1, 24), 1024], [gd(2012, 1, 25), 1024], [gd(2012, 1, 26), 1022], [gd(2012, 1, 27), 1031], [gd(2012, 1, 28), 1023], [gd(2012, 1, 29), 1019], [gd(2012, 1, 30), 1008], [gd(2012, 1, 31), 993]
];

function gd(year, month, day) {
    return new Date(year, month - 1, day).getTime();
}
        
再来就是把数据整理出来,我们建立一个叫做dataset的数组变量,并且插入3个对象分别代表上述提到的3个气象数据数据,在label属性分别设定成"Sea Level Pressure"、"Wind Speed"及"Temperature",在这里我们把Sea Level Pressure数据以柱形图呈现,所以要设定bars.show为true,并设定bars.barWidth,barWidth在这里因为我们是用时间格式数据,所以是以毫秒计算,而我们的数据间隔为1天,所以应该设定成24 * 60 * 60 * 600(乘以600而非1000是因为要让直条间有间距出来).

另外在WindSpeed数据对象里,因为我们要用折线图呈现,所以设定lines.show为true,另外也设定了数据点的属性points.symbol为"triangle",表示数据点符号为三角形,以及points.fillColor表示数据点要以什么颜色填满.而Temperature与Wind Speed数据对象设定是一样的.我们还在3组数据对象里都设定了不同的color属性,这样这3组数据就会以不同颜色呈现.

最后也是最重要的,因为我们要在一张图表里放上3组数据,所以y轴会出现3组刻度,这时就需要设定yaxis属性,在WindSpeed数据对象里可以看到我们设定了yaxis:2,表示WindSpeed是属于y轴第2组的,而Temperature数据对象里我们设定了yaxis:3,表示Temperature是属于y轴第3组的,不过,再回头去看Sea Level Pressure数据对象里我们并没有设定yaxis属性,这是因为Sea Level Pressure是属于y轴第1组的,若是第1组的则可以省略.yaxis属性跟等下我们要讲到的图表选项有很大的关系,下面会提到.

完整的dataset程序代码如下.
 
var dataset = [
    {
        label: "Sea Level Pressure",
        data: data3,         
        color: "#756600",
        bars: {
            show: true, 
            align: "center",
            barWidth: 24 * 60 * 60 * 600,
            lineWidth:1
        }
    }, {
        label: "Wind Speed",
        data: data2,
        yaxis: 2,
        color: "#0062FF",
        points: { symbol: "triangle", fillColor: "#0062FF", show: true },
        lines: {show:true}
    }, {
        label: "Temperature",
        data: data1,
        yaxis: 3,
        color: "#FF0000",
        points: { symbol: "circle", fillColor: "#FF0000", show: true },
        lines: { show: true }
    }
];        

建立图表选项

由于我们x轴是时间格式数据,所以xaxis.mode必须设定为"time",而xaxis.tickSize: [3, "day"]是表示刻度是以每3天的间隔显示,另外因为这范例中会出现柱形图,所以我们设定xaxis.tickLength为0,让x轴的刻度线不要显示,接着xaxis.axisLabel则是轴卷标的属性,最后再设定xaxis.color为"black",让刻度卷标以黑色显示.
 
     xaxis: {
        mode: "time",
        tickSize: [3, "day"],        
        tickLength: 0,
        axisLabel: "Date",
        axisLabelUseCanvas: true,
        axisLabelFontSizePixels: 12,
        axisLabelFontFamily: 'Verdana, Arial',
        axisLabelPadding: 10,
        color: "black"
    }
      
而y轴选项的部份是这个范例里的重点,这部份没设定好,你所绘制出来的图表可能就会乱七八糟.
一般来说只有1组数据的图表,只会有一个y轴刻度,如果我们要设定y轴的选项,就会用yaxis:{},而在这个范例里,我们用了3组数据,所以会出现3个y轴刻度,如果要设定y轴的选项时就必须改用yaxes:[]来代替,而数组中的第一个对象就代表在dataset里yaxis:1的对应,第二个对象就代表在dataset里yaxis:2的对应,依此类推.

默认y轴的刻度卷标都会出现在图表的左边,但这次我们有3个y轴的刻度卷标,都显示在左边会造成用户在看图表时的不易解读,所以我们要把2个刻度标签移到右边去,要达到这样的效果可以设定axis.position,可用的值有"left"和"right",另外我们也设定了axis.color为"black",让刻度卷标的文字都以黑色显示,当然我们还另外设定了轴标签(axisLabel).

最后我们在第一个对象里设定了max:1070,因为这对象是Sea Level Pressure的柱形图,如果没设定最大值的话,柱形图就会和其它2个折线图重迭,造成图表不易解读,所以我们设定了1070让柱形图保持显示在中间下方的地方,这样图表就会清爽多了.

y轴选项的程序代码如下.
 
     yaxes: [
        //yaxis:1
        {
            position: "left",
            max: 1070,
            color: "black",
            axisLabel: "Sea Level Pressure (hPa)",
            axisLabelUseCanvas: true,
            axisLabelFontSizePixels: 12,
            axisLabelFontFamily: 'Verdana, Arial',
            axisLabelPadding: 3            
        }, 
        //yaxis:2
        {
            position: "right",
            clolor: "black",
            axisLabel: "Wind Speed (km/hr)",
            axisLabelUseCanvas: true,
            axisLabelFontSizePixels: 12,
            axisLabelFontFamily: 'Verdana, Arial',
            axisLabelPadding: 3            
        },
        //yaxis:3
        {
            position: "right",
            color: "black",
            axisLabel: "Temperature (°C)",
            axisLabelUseCanvas: true,
            axisLabelFontSizePixels: 12,
            axisLabelFontFamily: 'Verdana, Arial',
            axisLabelPadding: 3            
        }
    ]

      
在图例的选项里我们设定了legend.noColumns为1,这样图例就会显示成一行,如果设定为0就会显示成一列,而legend.labelBoxBorderColor则设定了图例边框的颜色,再来就是legend.position我们设定成"nw"就是"north west",也就是左上角的意思.所以图例会显示在左上角.

网格的部份,我们设定了grid.hoverable为true,因为我们有使用提示框的功能,设定hoverable后,"plothover"事件才会被触发,提示框才会在我们鼠标移过数据点时显示.而grid.borderWidth则是图表边框的宽度,grid.backgroundColor则是设定背景颜色.
 
        legend: {
            noColumns: 1,
            labelBoxBorderColor: "#000000",
            position: "nw"        
        }

        grid: {
            hoverable: true,
            borderWidth: 3,        
            backgroundColor: { colors: ["#ffffff", "#EDF5FF"] }
        }
      

建立数据点提示框

提示框的作用是当你把鼠标移到图表上的数据点时,提示框会显示该数据点的详细数据,如x轴数值或y轴数值等,Flot的提示框必须自己实作出来.

首先我们必须绑定"plothover"事件,此事件会传入item对象,而我们所需要用到的数据都可以从这对象里抓出来,如item.series.label可抓出该数据点的标签名称,而item.datapoint可抓出x及y轴的数值等等,详细的提示框程序代码如下.
 
        var previousPoint = null, previousLabel = null;

        $.fn.UseTooltip = function () {
            $(this).bind("plothover", function (event, pos, item) {
                if (item) {
                    if ((previousLabel != item.series.label) || (previousPoint != item.dataIndex)) {
                        previousPoint = item.dataIndex;
                        previousLabel = item.series.label;
                        $("#tooltip").remove();

                        var x = item.datapoint[0];
                        var y = item.datapoint[1];

                        var color = item.series.color;
                        var date = "Jan " + new Date(x).getDate();
                        
                        var unit = "";

                        if (item.series.label == "Sea Level Pressure") {
                            unit = "hPa";
                        } else if (item.series.label == "Wind Speed") {
                            unit = "km/hr";
                        } else if (item.series.label == "Temperature") {
                            unit = "°C";
                        }

                        showTooltip(item.pageX, item.pageY, color,
                                    "" + item.series.label + "
" + date + " : " + y + " " + unit + ""); } } else { $("#tooltip").remove(); previousPoint = null; } }); }; function showTooltip(x, y, color, contents) { $('<div id="tooltip">' + contents + '</div>').css({ position: 'absolute', display: 'none', top: y - 40, left: x - 120, border: '2px solid ' + color, padding: '3px', 'font-size': '9px', 'border-radius': '5px', 'background-color': '#fff', 'font-family': 'Verdana, Arial, Helvetica, Tahoma, sans-serif', opacity: 0.9 }).appendTo("body").fadeIn(200); }

完成绘图

最后我们再呼叫$.plot把上面建立的dataset以及options带入,再呼叫UseTootip()后,整个绘图即完成.本范例完整的javascript程序代码可以在这里下载,有兴趣的可以下载回去研究看看.
 
        $(document).ready(function () {
            $.plot($("#flot-placeholder"), dataset, options);
            $("#flot-placeholder").UseTooltip();
        });
      

练习

本章的完整范例程序代码可以在这里找到并做在线练习 Go to Example Tester