图表控件Flot改造

 
 
 

 

不得不说,flot算是一个精巧的js绘制图表的插件。原型类似上图。下面列出一些API

网上有很多介绍其API的文章,有兴趣可以去cnblogs.com上去搜搜。目前,这个只是实现了,点线,区域图形,以及饼图,柱状图的绘制。一般情况下做一点简单的应用时可以的。但是,现在需要将其改造成另一个样子。

              背景区域绘制

              可以描点,并且可以根据不同点的数据,将点绘制为不同的颜色。

              根据数据序列的不同,以及数据序列的个数,绘制多张不同的控制图。传入几个数据序列绘制几张图。

              数据序列只是一个数组,横坐标为时间,纵坐标为值。纵坐标只显示UCLCLlCL三个数值

              多张控制图时,只有最后一张控制图显示时间横轴。

              只有第一张图需要根据数据点值的不同,设置点的颜色。

              ToolTips需要用数组传入,不使用其拼字符串的形式。

              小批换批时,根据传入的换批时间绘制换批提示线。

改造后效果为:

 

 

 

  绘制背景区域。

首先,影藏其网格线。需要将其源代码中drawGrid()方法的,绘制网格线部分注释掉。第1680到1764行。

它原有的绘制背景的方法,我们不适用,并且重写其方法。增加方法function drawBackground(options){}参数是innit时传入的options。

方法代码:

          var top=ticks[2];

          var center=ticks[1];

          var bottom=ticks[0];

          var percent=plotHeight/(max-min);

       

 

         //第一个空白区域高度为00到max-top

         var area1=(max-top)*percent;

           ctx.fillStyle = "#F8F8FF";

           ctx.fillRect(0, 0, plotWidth, area1);

 

             ctx.strokeStyle = "#696969";

             ctx.lineWidth = 0.5;//0, area3, plotWidth, smalltoparea

             ctx.moveTo(0, area1);

             ctx.lineTo(plotWidth, area1);

           //第二个区域为:area1到area2加上,(top-center)/3

           var smalltoparea=((top-center)/3)*percent;

              ctx.fillStyle = "rgb(250, 188, 186)";

                ctx.fillRect(0, area1, plotWidth, smalltoparea);

            //第三个区域

            var area2=2*smalltoparea;

              ctx.fillStyle = "rgb(251, 254, 188)";

           ctx.fillRect(0, area2, plotWidth, smalltoparea);

           // 第四个区域

         var area3=3*smalltoparea;

              ctx.fillStyle = "rgb(209, 254, 214)";

           ctx.fillRect(0, area3, plotWidth, smalltoparea);

           //CL下面每一个小区域高度

           var smallbottomarea=((center-bottom)/3)*percent;

           var area4=4*smalltoparea;

 

                        ctx.strokeStyle = "#696969";

                        ctx.lineWidth = 0.5;//0, area3, plotWidth, smalltoparea

                        ctx.moveTo(0, area4);

                        ctx.lineTo(plotWidth, area4);

                    

              ctx.fillStyle = "rgb(209, 254, 214)";

           ctx.fillRect(0, area4, plotWidth, smallbottomarea);

 

             var area5=area4+smallbottomarea;

              ctx.fillStyle = "rgb(251, 254, 188)";

           ctx.fillRect(0, area5, plotWidth, smallbottomarea);

 

 

           var area6=area5+smallbottomarea;

           ctx.fillStyle = "rgb(250, 188, 186)";

           ctx.fillRect(0, area6, plotWidth, smallbottomarea);

 

         

 

            var area7=area6+smallbottomarea;

             ctx.fillStyle = "#F8F8FF";

             ctx.fillRect(0, area7, plotWidth, smallbottomarea);

             ctx.strokeStyle = "#696969";

             ctx.lineWidth = 0.5;//0, area3, plotWidth, smalltoparea

             ctx.moveTo(0, area7);

             ctx.lineTo(plotWidth, area7);

                        ctx.stroke();

                          ctx.restore();

   

}

 

填充区域时,其小坐标原点在绘图区域左上角。需要根据比例算出需要在小坐标中绘制背景的高度。背景分为八个区,从坐标(0,0)点高度依次叠加。在draw()方法中调用drawbackground(options)方法。// if (!options.grid.backgroundImage) ctx.clearRect(0, 0, canvasWidth, canvasHeight);

        //   ctx.clearRect(0, 0, canvasWidth, canvasHeight);

 

            var grid = options.grid;

 

            // draw background, if any

            //if (grid.show && grid.backgroundColor)

                drawBackground(options);

绘制表格,绘制背景,绘制数据序列都在draw()方法中调用。

二、描点,根据不同的数据将点绘制为不同的颜色。后台根据不同的判异规格,将数据点序列判异后,返回一个与数据点一一对应的数组。数组中保存数据点是否被判异。不判异的点为0,判异的点位1。所有为0的点都使用正常颜色,为1的点使用红色。源代码从2124行开始。

   function drawSeriesPoints(series) {

            function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol,ispoint) {

                var points = datapoints.points, ps = datapoints.pointsize;

                //复制 产生新的ColorIndex数组

                var ColorArry=[];

                if (series.colorIndex!=null&&series.colorIndex.length>0) {

                   for (var i = 0; i < series.colorIndex.length; i++) {

    ColorArry.push(series.colorIndex[i]);

     ColorArry.push(series.colorIndex[i]);

//由于每个points都包含两个坐标轴的数据,先x轴,后y轴。一一对应的数据也需要成对出现

}

   

}

             

                for (var i = 0; i < points.length; i += ps) {

                    var x = points[i], y = points[i + 1];

                  //                    if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)

//                        continue;

                         if (x == null || x < axisx.min || x > axisx.max )//超出x轴的数据不绘图

                        continue;

                    //超出最大值的点,在绘制时将其值设为最大值,不会改变原有数据

                    if (y < axisy.min ) {

                          y= axisy.min ;

                            }

                    if (y > axisy.max) {

                           y=axisy.max;

                            }

                    

                    ctx.beginPath();

                    x = axisx.p2c(x);

                    y = axisy.p2c(y) + offset;

                    if (symbol == "circle")

                        ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);

                    else

                        symbol(ctx, x, y, radius, shadow);

                    ctx.closePath();

                    if (ColorArry[i]==1) {

                        ctx.fillStyle = "#FF0000";

                        ctx.strokeStyle="#FF0000";

                        ctx.fill();

                     

}

else {

       if (fillStyle) {

                        ctx.fillStyle =  series.color;

                        ctx.fill();

                    }

}

                

                    ctx.stroke();

 

                    if (ispoint) {

     ctx.strokeStyle=   series.color;

}

                  

                }

            }

           

            ctx.save();

            ctx.translate(plotOffset.left, plotOffset.top);

 

            var lw = series.points.lineWidth,

                sw = series.shadowSize,

                radius = series.points.radius,

                symbol = series.points.symbol;

            if (lw > 0 && sw > 0) {

                // draw shadow in two steps

                var w = sw / 2;

                ctx.lineWidth = w;

                ctx.strokeStyle = "rgba(0,0,0,0.1)";

                plotPoints(series.datapoints, radius, null, w + w/2, true,

                           series.xaxis, series.yaxis, symbol,false);

 

                ctx.strokeStyle = "rgba(0,0,0,0.2)";

                plotPoints(series.datapoints, radius, null, w/2, true,

                           series.xaxis, series.yaxis, symbol,false);

            }

 

            ctx.lineWidth = lw;

            ctx.strokeStyle = series.color;

            plotPoints(series.datapoints, radius,

                       getFillStyle(series.points, series.color), 0, false,

                       series.xaxis, series.yaxis, symbol,true);

            ctx.restore();

        }

 

我们改变了最大值点的绘制,原本如果数据点超出最大值时,是不绘制点的。此时我们将超出最大值的点的值设为最大值。那么,还需要更改其亮点之间的连接线。

方法为:(源代码1867行开始)

 

            function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {

                var points = datapoints.points,

                    ps = datapoints.pointsize,

                    prevx = null, prevy = null;

               

                ctx.beginPath();

                for (var i = ps; i < points.length; i += ps) {

                    var x1 = points[i - ps], y1 = points[i - ps + 1],

                        x2 = points[i], y2 = points[i + 1];

                   

                    if (x1 == null || x2 == null)

                        continue;

 

                    // clip with ymin

                    if (y1 <= y2 && y1 < axisy.min) {

                        if (y2 < axisy.min)

                        y2=axisy.min;

                          //  continue;   // line segment is outside

                        // compute new intersection point

                       // x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;

                        y1 = axisy.min;

                    }

                    else if (y2 <= y1 && y2 < axisy.min) {

                        if (y1 < axisy.min)

                        y1=axisy.min;

                          //  continue;

                       // x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;

                        y2 = axisy.min;

                    }

 

                    // clip with ymax

                    if (y1 >= y2 && y1 > axisy.max) {

                        if (y2 > axisy.max)

                        y2=axisy.max;

                         //   continue;

                       // x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;

                        y1 = axisy.max;

                    }

                    else if (y2 >= y1 && y2 > axisy.max) {

                        if (y1 > axisy.max)

                        y1=axisy.max;

                         //   continue;

                       // x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;

                        y2 = axisy.max;

                    }

 

                    // clip with xmin

                    if (x1 <= x2 && x1 < axisx.min) {

                        if (x2 < axisx.min)

                            continue;

                        y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;

                        x1 = axisx.min;

                    }

                    else if (x2 <= x1 && x2 < axisx.min) {

                        if (x1 < axisx.min)

                            continue;

                        y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;

                        x2 = axisx.min;

                    }

 

                    // clip with xmax

                    if (x1 >= x2 && x1 > axisx.max) {

                        if (x2 > axisx.max)

                            continue;

                        y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;

                        x1 = axisx.max;

                    }

                    else if (x2 >= x1 && x2 > axisx.max) {

                        if (x1 > axisx.max)

                            continue;

                        y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;

                        x2 = axisx.max;

                    }

 

                    if (x1 != prevx || y1 != prevy)

                        ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);

                   

                    prevx = x2;

                    prevy = y2;

                    ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);

                }

                ctx.stroke();

            }

 

        

由于先前绘制时,超出最大值的数据不绘制,所以当数据移到修改后的点时也不会显示ToolTip

源码2439行   function findNearbyItem(mouseX, mouseY, seriesFilter) 方法中,需要将超出最大值的数据点设为最大值。

2470行增加几行代码

                     if (y < axisy.min ) {

                        y= axisy.min ;

                             }

                        if (y > axisy.max) {

                              y=axisy.max;

                              }

 

然后直接获取数据位置,取其y值

function showTooltip(x, y, contents) {

 

 

        var ev = document.onmousemove;

        var mousePos = mousePosition(ev);

        y = mousePos.y;

        $('<div id="tooltip">' + contents + '</div>').css({

            position: 'absolute',

            display: 'none',

            top: y + 5,

            left: x + 5,

            border: '1px solid #fdd',

            padding: '2px',

            'background-color': '#fee',

            opacity: 0.80

        }).appendTo("body").fadeIn(200);

    }

 

获取鼠标位置:

function mousePosition(ev){

    var x, y;

    var ev = ev || window.event;

    return {

        x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,

        y:ev.clientY + document.body.scrollTop  - document.body.clientTop

    };

}

 

三、根据数据序列个数不同,绘制不同张数的控制图。目前Spc控制图最多有三张,至少有1长。所以将绘制方法包装一下即可。

所有的数据序列,时间,自定义的tooltips等都已参数传入。并计算其最大最小值。最没有技术的就是这个部分….

function DrawChart(chartcount, spec1, spec2, spec3, data1, reddotArry, data2, data3, timeArry, batchChangeTime, Tips1, Tips2, Tips3, placeholder1, placeholder2, placeholder3) {

    var d1 = [];

    var d2 = [];

    var d3 = [];

    var changeTime = [];

    var maxspec1 = 0;

    var minspec1 = 0;

    var specMaxLength

    if (spec1 != null && spec1.length > 0) {

        maxspec1 = spec1[2] + (spec1[2] - spec1[1]) / 3;

        minspec1 = spec1[0] -(spec1[1] - spec1[0]) / 3;

    }

    var maxspec2 = 0;

    var minspec2 = 0;

    if (spec2 != null && spec2.length > 0) {

        maxspec2 = spec2[2] + (spec2[2] - spec2[1]) / 3;

        minspec2 = spec2[0] - (spec2[1] - spec2[0]) / 3;

    }

    var maxspec3 = 0;

    var minspec3 = 0;

    if (spec3 != null && spec3.length > 0) {

        maxspec3 = spec3[2] + (spec3[2] - spec3[1]) / 3;

        minspec3= spec3[0] - (spec3[1] - spec3[0]) / 3;

    }

 

 

 

 

    if (timeArry != null && timeArry.length > 0) {

        for (var i = 0; i < data1.length; i++) {

            d1.push([GetTimeStep(new Date(timeArry[i])), data1[i]]);

        }

        for (var i = 0; i < data2.length; i++) {

            d2.push([GetTimeStep(new Date(timeArry[i])), data2[i]]);

        }

        for (var i = 0; i < data3.length; i++) {

            d3.push([GetTimeStep(new Date(timeArry[i])), data3[i]]);

        }

    }

 

 

    if (batchChangeTime != null && batchChangeTime.length>0) {

        for (var i = 0; i < batchChangeTime.length; i++) {

            changeTime.push(GetTimeStep(new Date(batchChangeTime[i])));

        }

    }

 

    if (chartcount == 1 || (placeholder2 == "" && placeholder3 == "")) {

        DrawLastChart(spec3, maxspec3, minspec3, d3, changeTime, Tips3, reddotArry, placeholder3, timeArry);

        $(placeholder1).hide();

        $(placeholder2).hide();

    }

    else if (chartcount == 2 || placeholder3 == "") {

        plot3 = DrawLastChart(spec3, maxspec3, minspec3, d3, changeTime, Tips3, null, placeholder3);

        DrawFirstChart(spec1, maxspec1, minspec1, d1, changeTime, Tips1, reddotArry, placeholder1,plot3);

     

        $(placeholder2).hide();

    }

    else if (chartcount == 3) {

       plot2= DrawCenterChart(spec2, maxspec2, minspec2, d2, changeTime, Tips2, placeholder2);

       plot3= DrawLastChart(spec3, maxspec3, minspec3, d3, changeTime, Tips3, null, placeholder3);

        DrawFirstChart(spec1, maxspec1, minspec1, d1, changeTime, Tips1, reddotArry, placeholder1,plot2,plot3);

      

    }

    else {

        placeholder1.hide();

        placeholder2.hide();

        placeholder3.hide();

    }

 

 

}

 

然后调用绘制控制图的方法即可。第一张图:需要根据数据点的不同标红。第二张:不需要横坐标。但是数据要与第一张图一一对应。第三张图:需要横坐标,而且第三张图,有时需要当第一张图使用。所以需要三种绘图方法。DrawFirstChart();DrawCenterChart();DrawLastChart();

具体代码在后面贴出。

三张图数据点的一一对应。

  三张图的绘制时间是一致的。即同一时间会在三张图中绘制三个点。此时,如果不做处理,绘制出来的数据是有偏移的——与时间不能对应。有两种方式,可以设置坐标轴的小数点位置,但是会出现以下情况:

0.0000或者,23.1220与3.1200的位置依旧有偏移。

 固定左边坐标轴位置:擦,还要改源码

877行  function measureTickLabels(axis)方法中修改923行

                if (labels.length > 0) {

                    dummyDiv = makeDummyDiv(labels, "");

                    if (w == null)

                    w=40;

                       // w = dummyDiv.children().width();

                    if (h == null)

                        h = dummyDiv.find("div.tickLabel").height();

                    dummyDiv.remove();

                }

将w设置为固定宽度。同时修改背景绘制区域的起始位置,否则坐标轴会被绘制在背景区域中。

修改228行  checkload()方法

function checkload() {

              if(options.grid.backgroundImage)

                     if ( bgimage.complete ) {               

                            ctx.save();

                            ctx.restore();

             

                 var pp = ctx.createPattern(bgimage, 'repeat');

                 ctx.fillStyle = pp;

                 ctx.fillRect(45, 0, canvasWidth-47, canvasHeight);

                   draw();

            }

                     else

                            setTimeout( checkload, 100 ) ;

       }

 

 

 

四:y轴可以直接设置yaxis: { ticks: spec ,min:minspec,max:maxspec},

但是x轴的时间需要先转换为UTC再放到数组中。2012/4/12 12:12:12格式转换为UTC代码为

function GetTimeStep(time) {

    day = time.getHours();

 

    time = time.setHours(8 + day);

 

    time = new Date(time);

 

    time = time.getTime();

 

    return time;

 

}

五、只有最后一张显示时间横轴,

  xaxis: { mode: "time", timeformat: "%h:%M", minTickSize: [1, "minute"], show: false },

把不需要x轴的控制图show:FALSE即可。

 

7,切换批次时,根据传入的换批时间绘制换批提示线。增加方法

 

function drawBatchChangeLine(series){

            ctx.save();

            ctx.translate(plotOffset.left, plotOffset.top);

            var axisx=series.xaxis;

            var axisy=series.yaxis;

             var ticks=axisy.ticks;

              var max=axisy.max;

              var min=axisy.min;

              var changeTime=series.BatchChangeTime;

              for (var i = 0; i <changeTime. length; i++) {

                   ctx.moveTo(axisx.p2c(changeTime[i]) , 0);

                    ctx.lineTo(axisx.p2c(changeTime[i]) , plotHeight );

             ctx.stroke();

                       

 

 

}

            

           

                //ctx.stroke();

                   ctx.restore();

        }

该方法在drawSeries()时被调用

 

  function drawSeries(series) {

            if (series.lines.show)

                drawSeriesLines(series);

            if (series.bars.show)

                drawSeriesBars(series);

            if (series.points.show)

                drawSeriesPoints(series);

                drawBatchChangeLine(series);

        }

 

到此,改造基本结束

 

 

 

图中黑线为换批线。红色的竖线为校准线,会随鼠标移动而移动。

 

 

posted @ 2012-05-24 10:09  小兔兔  阅读(1540)  评论(0编辑  收藏  举报