如何畫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