ECharts實現動態曲線(中)

ECharts實現動態曲線(上),模擬數據是在瀏覽器端生成的。
現在,我們這樣做:把模擬數據的生產放到服務器端,瀏覽器通過Ajax發送請求獲取服務端數據。

效果圖

在這裏插入圖片描述

實現

服務器使用了一個定時器。只要服務器一啓動,定時器就會開始工作,每隔1000ms生成一次新數據。
瀏覽器端也使用了一個定時器,點擊“開始”按鈕,該定時器纔會開啓,每間隔250ms就會向服務端發送一次請求,以獲取新數據來更新圖表。

遺留問題

服務端定時器的時間間隔是1000ms,瀏覽器這邊定時器的時間間隔是250ms。忽略因不同步可能導致的時間差,理論上講,1s內,瀏覽器能發出4次請求,服務器因此返回4份響應。就像這樣,
在這裏插入圖片描述
現在遇到這麼一個問題:在圖表動態更新的過程中,切換到了其他標籤頁,瀏覽器裏定時器的工作受到了影響,即1s內,瀏覽器只能發出了1次請求,服務器因此只返回1份響應。更糟糕的事情是,曲線上出現斷點,丟數據了。不過,切回到原標籤頁,定時器又恢復正常了。
在這裏插入圖片描述
想想辦法吧。

目錄結構

在這裏插入圖片描述

瀏覽器端

//index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>動態曲線</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"
            integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
            crossorigin="anonymous">
    </script>
    <link rel="stylesheet" href="./index.css">
</head>
<body>
    <div id="myChart"></div>
    <div class="container">
        <div class="buttons">
            <div class="button" id="start">開始</div>
            <div class="button" id="end"> 結束</div>
        </div>
    </div>
<script src="./MyButton.js"></script>
<script src="./MyChart.js"></script>
<script src="./index.js"></script>
</body>
</html>
//index.js
initButtons();
const myChart = new MyChart(document.getElementById("myChart"));
myChart.init();

function initButtons(){
    var timer;
    const startBtn = new MyButton(document.querySelector("#start"));
    startBtn.addEventListener("click",function(){
        timer = startTimer();
        startBtn.disable();
    });
    
    const endBtn = new MyButton(document.querySelector("#end"));
    endBtn.addEventListener("click",function(){
        closeTimer(timer);
        startBtn.enable();
    });
}
function startTimer(){
    return timer = setInterval(function(){
        myChart.getDataFromServer();
    },250,myChart);
}
function closeTimer(timer){
    clearInterval(timer)
}

//MyButton.js
(function(win){
    function MyButton(elm){
        this.elm = elm; 
    }
    MyButton.prototype.addEventListener = function(type,handler){
        if(this.elm.addEventListener){
            this.elm.addEventListener(type,handler,false);
        }else {
            this.elm["on"+type] = handler;
        }
    }
    MyButton.prototype.disable = function(){
        this.elm.classList.add("disabled");
    }
    MyButton.prototype.enable = function(){
        this.elm.classList.remove("disabled");
    }
    win.MyButton = MyButton;
}(window));
//MyChart.js
(function(win){
    function MyChart(elm){
        this.elm = elm;
        this.chart = echarts.init(elm); 
        this.chart.gap = 40;
    }
    MyChart.prototype.init = function(){
        const times = [];
        const values = [];
        const gap = this.chart.gap;
        const option = {
            grid:{
                bottom:60,
                top:gap,
                left:gap,
                right:gap,                
            },
            xAxis: {
                type: 'category',
                data:times,
                axisLabel:{
                    rotate:45
                },
                axisTick: {
                    alignWithLabel: true
                },
                name:"時間"
                
            },
            yAxis: {
                type: 'value',
                min:0,
                max:1,
                name:"帶寬利用率"
            },
            series: [{
                data:values,
                type: 'line',
                smooth: true
            }],
            tooltip: {
                trigger: 'axis',
                axisPointer: {       
                    type:"line"      
                }
            }
        };
        this.chart.setOption(option);
    }
    MyChart.prototype.update = function(data){
        const {times,values} = data;
        const formatedTimes = times.map(t => formatTime(new Date(t)));
        console.log(formatedTimes);
        this.chart.setOption({
            xAxis:{
                data:formatedTimes
            },
            series:[{
                data:values
            }]
        });  
    }

    MyChart.prototype.getDataFromServer = function(){
        const myChart = this;
        $.ajax({
            type:"get",
            url:"/getData",
            success:function(res){
                myChart.update(res);
            }
        })
    }

    function formatTime(time){
        const hour = time.getHours();
        const min = time.getMinutes();
        const sec = time.getSeconds();
        const h = hour<10 ? "0"+hour : hour;
        const m = min<10  ? "0"+min : min;
        const s = sec<10 ? "0"+sec : sec;
        return h+":"+m+":"+s;
    }

    win.MyChart = MyChart;
}(window))
//index.css
#myChart{
    display:inline-block;
    width:400px;height:200px;
    border:1px solid lightgray;
    border-right:none;
    vertical-align:middle;
}
.container{
    display:inline-block;
    width:80px;height:200px;
    border:1px solid lightgray;
    border-left:none;
    margin:-5px;
    vertical-align:middle;

}
.buttons{
    display:table-cell;
    width:inherit;height: inherit;
    vertical-align: middle;
}
.button{
    font-size:0.75em;
    padding:.3em;
    background-color:rgba(0,0,255,.5);
    border:1px solid transparent;
    border-radius:.5em;
    box-shadow:1px 1px 1px black;
    text-align:center;
    margin:1em;
}
.button:hover{
    cursor:pointer;
    background-color:rgba(0,0,255,1);
    color:white;
}
.disabled{
    background-color:lightgray;
    color:lavender;
    box-shadow:1px 1px 1px lightslategray;
}
.disabled:hover{
    cursor:not-allowed;
    background-color:lightgray;
    color:lavender;
}

服務器端

//server.js
const DataCreator = require("./DataCreator.js");
const dc = new DataCreator();
dc.startTimer();
const express = require("express");
const server = express();
const path = require("path");
server.use(express.static(path.join(__dirname,"src")));
server.get("/getData",function(req,res){
    res.writeHead(200,{"Content-Type":"application/json"});
    const {times,values} = dc;
    res.end(JSON.stringify({
        times,
        values
    }));
});
server.listen(3000,function(){
    console.log("listening on*:3000");
})
//DataCreator.js
function DataCreator(){
    this.dotNum = 10;
    this.interval = 1000;
    this.lastStartTime = undefined;
    this.lastValues = [];
    this.times = [];
    this.values = [];
}
DataCreator.prototype.createTimes = function(){
    let {lastStartTime,dotNum,interval} = this;
    let startTime = lastStartTime?lastStartTime:new Date().getTime();
    this.lastStartTime = startTime + interval;

    let times = [];        
    for(var i=0;i<dotNum;++i){
        var time = new Date(startTime+i*interval);
        times.push(time);
    }
    return times;   
}
DataCreator.prototype.createValues = function(){
    let {lastValues,dotNum} = this;
    let values;
    if(lastValues.length === 0){
        values = [];
        for(var i=0;i<dotNum;++i){
            var value = parseFloat(Math.random().toFixed(2));
            values.push(value);
        }
    }else {
        values = lastValues.slice(1);
        values.push(parseFloat(Math.random().toFixed(2)));
    }
    this.lastValues = values.slice();
    return values;
}
DataCreator.prototype.initData = function(){
    var startTime = new Date().getTime();
    this.times = this.createTimes();
    this.values = this.createValues();   
}
DataCreator.prototype.startTimer = function(){
    this.initData();
    const self = this;
    const {interval} = self;
    const fn = function(){
        self.times = self.createTimes();
        self.values = self.createValues();  
        timer = setTimeout(fn,interval); 
    };
    fn();
}

module.exports = DataCreator;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章