zoukankan      html  css  js  c++  java
  • 图表控件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);

            }

    到此,改造基本结束

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

     

  • 相关阅读:
    sa-token v1.9.0 版本已发布,带来激动人心新特性:同端互斥登录
    为什么类只能用public修饰?
    Sentinel导航
    Ribbon导航
    Feign导航
    Gateway导航
    Nacos导航
    Nginx导航
    微服务导航
    Springboot之登录模块探索(含Token,验证码,网络安全等知识)
  • 原文地址:https://www.cnblogs.com/hexinxiaoyao/p/hx1.html
Copyright © 2011-2022 走看看