zoukankan      html  css  js  c++  java
  • 模拟实时动态趋势

      主要用到echart插件实现趋势图绘制,为了模拟实时效果,并且保证趋势图匀速移动,实现主要思想如下:

    1.为保证匀速移动,趋势图的移动以前端时间间隔为准,时间间隔到了,趋势图就移动一次,不管后台数据正常返回与否,有后台返回的数据则显示正常数据,没有正常数据则前端自行填补数据。

    2.趋势图x轴显示的时间,是第一次和后台交互,从时间戳解析出来的。最新的时间,就是取第一次请求数据中后台最新的时间,以后的时间,由前端自行计算nextTime,所以第一次与后台的协定,特别重要。

    3.初始化整个趋势图后,以后一份份数据请求。后台以“尽最大努力”发送数据位目标,前端indexRight记录最新最近的正常返回数据。

    4.cacheData数组用来缓存后台数据,生成趋势图时,参考缓存数据,并通过一些“填空补缺”的机制,绘制趋势图。

    5.趋势图为了保证轴两端都有时间,对xAxis 的interval设置函数实现。

    趋势图部分:

      1 var trendChart = null;
      2 var initTrendFlag = false;
      3 /**echart反复创建**/
      4 function createTrend(convertedData) {
      5     if(convertedData && convertedData[0] && convertedData[1]){
      6         $("#trendContent").attr('class','trendContentStyle');
      7         var title = "";
      8         var historyOption =    {
      9             title : {
     10                 text: title
     11             },
     12             tooltip : {
     13                 trigger: 'axis',
     14                 formatter:"{b}<br>{c}"
     15             },
     16             legend: {
     17                 show:false,
     18                 data:['']
     19             },
     20             dataZoom : {
     21                 show : false,
     22                 start : 0,
     23                 end : 100
     24             },
     25             xAxis : [
     26                 {
     27                     type : 'category',
     28                     boundaryGap : false,
     29                     axisLabel: {
     30                         show: true,
     31                         rotate: 0,
     32                         interval: function(index,data){
     33                             if(index == 0|| index == showNum-1 || index % 40 == 0){
     34                                 return true;
     35                             }else
     36                             {
     37                                 return false;
     38                             }
     39                         },
     40                         onGap: true
     41                     },
     42                     data : convertedData[0] //[0,0,0,0,0,0,0,0,0,0]
     43                 }
     44             ],
     45             yAxis : [
     46                 {
     47                     show:false,
     48                     type : 'value',
     49                     scale: true,
     50                     name : '价格',
     51                     boundaryGap: [0.2, 0.2],
     52                     min:0
     53                 }
     54             ],
     55             series : [
     56                 {
     57                     itemStyle: {normal: {areaStyle: {type: 'default',color:'#D8E2F7'}}},
     58                     symbol:'circle',
     59                     symbolSize:0.02,
     60                     name:'',
     61                     smooth:true,
     62                     type:'line',
     63                     data:convertedData[1] //[0,0,0,0,0,0,0,0,0,0]
     64                 }
     65             ]
     66         };
     67             //切换到有数据清空下,重新set整个option
     68 
     69             trendChart = echarts.init(document.getElementById('trendContent'));
     70             trendChart.setOption(historyOption);
     71     }else{
     72        if(trendChart && trendChart.clear){
     73            trendChart.clear();//从有数据切换到无数据
     74        }
     75         $("#trendContent").attr('class','trendContentStyle nodata');
     76     }
     77 }
     78 
     79 function initTrend(poiid){
     80     cacheData = [];//切换地区,清空上一次缓存数据
     81     initTrendFlag = false;//初始化标识为false
     82     var url = "/lte/realtimeheatmap/poilinkchart";
     83     var para = {
     84         province:condition.provincecode,
     85         city:condition.citycode,
     86 //        poiid:'g20atblc',
     87         poiid:poiid,
     88         time:""
     89     };
     90 //"/lte/realtimeheatmap/poilinkchart?province=330000&city=330100&poiid=g20atblc&time=2016-07-10+14:50:00"
     91     $.ajax({
     92         type: "GET",
     93         contentType: "application/json",
     94         url: url,
     95         data: para,
     96         async: false, //需要同步;初始化成功后,趋势图再做其他
     97         success: function (data) {
     98             //初始化成功,改变indexNow;缓存、更新cacheData,不绘制趋势图
     99 //            showNum = data.length;//第一次初始化,数据就有可能缺失
    100             if(!data || data.length <1){
    101                 createTrend(null);
    102                 return;
    103             }
    104             var newestTime = data[data.length-1][0];
    105             if(indexRight < newestTime){
    106                 indexRight = newestTime;//第一次初始化,indexRight = "" ,一定会成功赋值的
    107             }
    108             indexNow = indexRight;
    109             indexTail = getNextTimeStamp(indexNow,timeInterval,-showNum + 1);//显示[indexTail,indexNow]showNum个数据
    110             initCache(data);//用第一次数据,初始化规整的cacheData
    111             var convertedData = convertTrendData();//结合cacheData中数据,生成趋势图所需数据
    112             createTrend(convertedData);
    113             initTrendFlag = true;
    114             requestTrendData();//初始化之后,立马发起一次请求
    115         },
    116         error: function (XMLHttpRequest, textStatus, errorThrown) {
    117             createTrend(null);
    118             initTrendFlag = false;
    119         }
    120     });
    121 }
    122 
    123 /**不断定时调用,发请求,请求最新的数据,来生成趋势图**/
    124 function requestTrendData() {
    125     if(!cacheData || cacheData.length < 1) return;//初始化失败
    126 //    var indexTail = getNextTimeStamp(indexNow,timeInterval,-showNum);
    127     if(indexRight < indexTail){
    128         indexRight = indexTail;//整个趋势图显示数据都是前端补齐的
    129     }else if(indexRight > indexNow){
    130         indexRight = indexNow;//某一次最新数据超过indexNow
    131     }
    132     var url = "/lte/realtimeheatmap/poilinkchart";
    133     //"/poilinkchart?province=330000&city=330100&poiid=g20atblc&time=2016-07-10+14:50:00"
    134     var para = {
    135         province:condition.provincecode,
    136         city:condition.citycode,
    137         poiid:'g20atblc',
    138         time:indexRight
    139     };
    140 
    141     $.ajax({
    142         type: "GET",
    143         contentType: "application/json",
    144         url: url,
    145         data: para,
    146         //async: false, //需要异步;不过,存在几个ajax返回同事addData去修改cacheData的情况?
    147         success: function (data) {
    148             /**拿到数据,填装数组,绘制趋势图,更新indexRight**/
    149             if(!data || data.length <1){
    150                 //查无数据,返回 []
    151                 return;
    152             }
    153             var newestTime = data[data.length-1][0];
    154             if(indexRight < newestTime){
    155                 indexRight = newestTime;//indexRight只右移;不再次查询之前的
    156             }
    157             addResponseData(data);    //更新cacheData;不绘制趋势图;更新indexTail、indexNow
    158 //            var convertedData = convertTrendData(cacheData);
    159 //            createTrend(convertedData);
    160         },
    161         error: function (XMLHttpRequest, textStatus, errorThrown) {
    162              /**ajax失败,添加测试数据;真实情况ajax失败直接不予处理,时间间隔到了,前端就走**/
    163          //   addResponseData([[getNextTimeStamp(indexNow,timeInterval,1),Math.random()*100]]);
    164         }
    165     });
    166 }

    缓存数据部分:

    /**趋势图只有第一次初始化成功后,才开始持续走动;在初始化成功的ajax回调里面调用其他函数**/
    
    //记录收到的最新一次的  真实后台数据的时间戳 ,超期了,等于indexTail
    // 为了填补(连续缺数据的情况),发给后台;如果30分钟的到了,25分钟的还是前端填补的,那直接忽略
    var indexRight = "";//在有新数据来到的时候更新
    var indexNow = "";//指向趋势图  显示的  最新时间戳;只在前端时间间隔到了的时候更新
    var indexTail = "";//指向趋势图 显示的 最早时间戳;只在前端时间间隔到了的时候更新
    var timeInterval = 1;//时间间隔,五分钟,可配置
    var showNum = 481;//前后端提前协议好,因为第一次初始化的时候,数据长度就不确定;这样保证indexNow和indexTail间距固定
    //存储ajax返回的趋势图数据;不存储伪造数据
    var cacheData = [];//cacheData的第一个数据cacheData[0][0],不一定对齐显示的最左侧数据indexTail,最后一个数据,也不一定对齐indexNow(可能缓存未来的)
    //cacheData[0] = [];//先初始化第一个元素为一个空的数组
    
    
    //var mapCachedata = {};//日期-数据,key-value形式,更好填充?
    /**构造一个ajax返回的趋势图样例数据**/
    /**先假设趋势图显示10分钟数据,时间间隔为1分钟**/
     var initData = [
        ["2016-07-01 08:30:00",50],
        ["2016-07-01 08:31:00",40],
        ["2016-07-01 08:32:00",70],
        ["2016-07-01 08:33:00",55],
        ["2016-07-01 08:34:00",55],
        ["2016-07-01 08:35:00",25],
        ["2016-07-01 08:36:00",155],
        ["2016-07-01 08:37:00",15],
        ["2016-07-01 08:38:00",95],
        ["2016-07-01 08:39:00",52]
    ];
    
    /**第一次收到数据后,形成规则的cacheData数组**/
    function initCache(resData){
        var timeStamp = indexTail;
        var searchData = null;
        var flag = false;
        for(var k=0;k<showNum;k++){
            cacheData[k] = [];
            cacheData[k][0] = timeStamp;
            cacheData[k][1] = 0;
            //遍历resData上的数据,如果ajax返回数据里面有,直接填上,如果没有填补之前的
            searchData = getSearchData(timeStamp,resData);
            if(searchData != null){
                flag = true;//一旦有一条有一条效数据,后续空缺填补之前的
                cacheData[k][1] = searchData;
            }else if(flag){
                //前段自行填补
               cacheData[k][1] = cacheData[k-1][1];
            }
            timeStamp = getNextTimeStamp(timeStamp,timeInterval,1);
        }
    }
    
    /**需要把cacheData从稀疏数组,转化为稠密数组吗?
     * cacheData前段,在初始化正确后,就一直会保证是非稀疏的
     * cacheData[i]为undefined的空位不能剔除掉,有可能有数据会来填补
     * **/
    function fillUpCache() {
        if (!cacheData || !cacheData[0] || !cacheData[0][0]) {
            {
                //cacheData第一个数据为空时;修改头部为indexTail,0
                cacheData = [];
                cacheData[0] = [];
                cacheData[0][0] = indexTail;
                cacheData[0][1] = 0;
    //         cacheData.unshift([indexTail,0]);//在头部插入一个数据;这样会多
            }
    
            for (var i = 1; i < cacheData.length; i++) {
                /**这样只能保证数组为没有返回的数据预留了空位的情况下的填充;如果第一次返回数据就是间断的**/
                if (cacheData[i] == undefined) {
                    cacheData[i] = [];
                    cacheData[i][0] = getNextTimeStamp(cacheData[i - 1][0], timeInterval, 1);//要确保第一个非空;i从1开始
                    cacheData[i][1] = cacheData[i - 1][1];
                }
            }
        }
    }
    /**从cacheData取数据给趋势图加载;
     * 不存在则伪造;填补cacheData
     * 行列转换,转化时间戳为显示时间形式**/
    function convertTrendData(){
        var time = [];
        var ueNum = [];
        var timeStamp = indexTail;
        var searchData = null;
        for(var k=0;k<showNum;k++){
            var timeArr = timeStamp.split(" ");
            if(timeArr && timeArr[1]){
                var lastTime = timeStamp.split(" ")[1];
                if(lastTime[0] == '0'){
                    lastTime = lastTime.substring(1);
                }
                time[k] = lastTime;
            }else{
                time[k] = timeStamp;//时间戳格式有误;直接赋值整个时间戳
            }
            ueNum[k] = 0;
            //遍历cacheData上的数据,如果缓存里面有,直接填上,如果没有填补之前的
            searchData = getSearchData(timeStamp,cacheData);//缓存上基本有序,按理说,可以优化搜索,不应该每次都去遍历所有
            if(searchData != null){
                ueNum[k] = searchData;
            }else{
                if(k>0){
                    ueNum[k] = ueNum[k-1];//不是第一个数据,就取前一个数据的
                    addResponseData([[timeStamp,ueNum[k]]]);//伪造的数据,也必须纳入缓存
                }else{
                    ueNum[k] = 0;//第一个数据,没有历史,直接给0;第一次初始化cacheData成功后,不会搜索不到
                }
            }
            timeStamp = getNextTimeStamp(timeStamp,timeInterval,1);
        }
            return [time,ueNum];
    
    }
    /**时间到了,填入之前的数据;并且从头到尾检查数组是否有空缺,有空缺填补上一个**/
    
    
    /**填装数据,有ajax返回数据需要填充,时间间隔 到了也需要填充;添加新数据到尾部,drop头数据**/
    
    /**ajax返回的数据,是合理时间点,有效数据;存在三种情况:填补(更新)之前数据,填充当前最新数据,或者缓存未来数据**/
    function addResponseData(reqData){
            /**返回数据是“连续时间点”的**/
        var time = null;
        var data = null;
        var insertIndex = null;
        if(reqData && reqData.length > 0){
            for(var i=0;i<reqData.length;i++){
                if(reqData[i] && reqData[i][0] && reqData[i][1]){
                    time = reqData[i][0];
                    data = reqData[i][1];
                    insertIndex = getIndexByTime(time);
                    if(insertIndex !== null){
                        cacheData[insertIndex] = [];
                        cacheData[insertIndex][0] = time;
                        cacheData[insertIndex][1] = data;
                    }
                }
            }
            /**添加新数据后,暂时不挪动indexNow等;每次都在前端时钟结束,调用move方法移动**/
    
        }
    }
    
    /**前端计时器到时间间隔,坐标轴移动一个单位,绘制趋势图**/
    function trendMove(){
        indexNow = getNextTimeStamp(indexNow,timeInterval,1);
        indexTail = getNextTimeStamp(indexTail,timeInterval,1);
        if(cacheData && cacheData[0] && cacheData[0][0]){
            //如果cacheData首个元素为 undefined 或者[] 则无法删除数据了;
            /** 在第一次初始化的时候,如果首个元素正常,后续每挪动一步,必有填补,cacheData在indexTail-indexNow之间的区段是非稀疏的数组
             * 首个元素就不可能为空了**/
            while(getNextTimeStamp(indexTail,timeInterval,-1) > cacheData[0][0]){
                cacheData.shift();//删除超期数据
            }
        }
        var convertedData = convertTrendData();//结合cacheData中数据,生成趋势图所需数据
        createTrend(convertedData);
    }
    
    /**初始化的时候,后台需要返回时间戳,例如2016-07-08 7:25   **/
    function getNextTimeStamp(currentTime,timeInterval,num){
        var d = new Date(currentTime);
        d.setMinutes(d.getMinutes() + timeInterval * num);
        var nextStr = getTimeStamp(d)[0];//暂时先用完整时间"2016-07-08 7:25"
        return nextStr;
    }
    
    /**根据给定时间戳,查找数据应该填入cacheData位置;更新index;跳跃式更新,数组中间的是undefined,数组长度会变长+N**/
    function getIndexByTime(timeStamp){
        if(timeStamp < indexTail){
            //过期数据
            return null;
        }else if(timeStamp <= indexNow){
            //趋势图正在显示的历史时间
            //折半查找不行,因为可能中间的数据是undefined 无法确定是在哪段
            for(var tail = cacheData.length - 1;tail >=0;tail--){
                if(cacheData[tail] && cacheData[tail][0] && cacheData[tail][0] === timeStamp){
                    return tail;
                }
            }
            return null;//数据异常,没匹配上各个时间点,一般不应该出现
        }else{
            //未来数据
            var tempTimeStamp = indexNow;
            var tempIndex = cacheData.length - 1;
            while(timeStamp > tempTimeStamp){
                tempTimeStamp = getNextTimeStamp(tempTimeStamp,timeInterval,1);//后移一个间隔
                tempIndex ++;
                if(timeStamp === tempTimeStamp){
                    return tempIndex;
                }
            }
            /**数据间隔应该可以逐步+1 timeInterval吻合,一般不应该返回null;除非本身cacheData时间点“不连续”**/
            return null;
        }
    }
    
    /**为填充趋势图数据,在cacheData中查找数据**/
    function getSearchData(timeStamp,cacheData){
        //折半查找行不通,虽然有序,但是cacheData可能是稀疏数组
    //    if(!cacheData || !cacheData.length) return null;
        for(var index=0;index<cacheData.length;index++){
            if(cacheData[index] && cacheData[index][0] && cacheData[index][0] === timeStamp && cacheData[index][1] != undefined){
                return cacheData[index][1];//"1a3"这种非法数据没有排除
            }
        }
        return null;//数据没有缓存在cacheData中
    
    }
    
    //将日期对象转化为标准的时间戳以及趋势图横坐标显示形式:["2016-07-08 7:25","7:25"]
    function getTimeStamp(dateObj){
        var strYear = dateObj.getFullYear();
        var strMonth = dateObj.getMonth() + 1;
        if (strMonth < 10) {
            strMonth = '0' + strMonth;
        }
        var strDay = dateObj.getDate();
        if (strDay < 10) {
            strDay = '0' + strDay;
        }
        var strHour = dateObj.getHours();
        if(strHour < 10){
            strHour = "0" + strHour;
        }
        var strMinute = dateObj.getMinutes();
        if(strMinute < 10){
            strMinute = '0' + strMinute;
        }
       
        var last = strHour + ":" + strMinute + ":00";
        var all = strYear + '-' + strMonth + '-' + strDay + " " + last;
        return [all,last];
    }
  • 相关阅读:
    将其他js类库制作成seajs模块
    Jquery 数组操作
    jquery 数组 添加元素
    Asp.net core MVC paypal支付、回调——app支付
    最近接触的几种APP支付方式——支付宝支付
    最近接触的几种APP支付方式——信用卡支付AuthorizeNet
    最近接触的几种APP支付方式——微信app支付
    微信选择图片、上传图片、下载图片、扫一扫接口调用源码
    Oracle连接查询语句
    在ps中,怎么把图片的一部分剪切出来,创建一个新的图层?
  • 原文地址:https://www.cnblogs.com/yongwangzhiqian/p/5671630.html
Copyright © 2011-2022 走看看