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];
    }
  • 相关阅读:
    1451. Rearrange Words in a Sentence
    1450. Number of Students Doing Homework at a Given Time
    1452. People Whose List of Favorite Companies Is Not a Subset of Another List
    1447. Simplified Fractions
    1446. Consecutive Characters
    1448. Count Good Nodes in Binary Tree
    709. To Lower Case
    211. Add and Search Word
    918. Maximum Sum Circular Subarray
    lua 时间戳和时间互转
  • 原文地址:https://www.cnblogs.com/yongwangzhiqian/p/5671630.html
Copyright © 2011-2022 走看看