【问题标题】:Drawing a d3 realtime line chart inside socket.on (socket io)在 socket.on (socket io) 中绘制 d3 实时折线图
【发布时间】:2016-08-10 09:03:32
【问题描述】:

我正在使用通过套接字 io 从服务器接收的值,并且我想制作一个在新值到达时实时更新的图表。

为了绘制图表,我使用了此示例中的代码:http://jsfiddle.net/chrisJamesC/YruDh/ 我收到以下表格的数据:

 socket.on('news', function (data) { /*I make the chart here*/...});

我将next() 函数中的value 字段替换为我自己的值,即我从套接字接收的值,并且一切正常。

唯一的问题是,每当一个新的数据点到达时,每两秒,不仅图表会更新,而且我会在浏览器中获得相同图表的精确副本,位于现有图表下方。每次我收到一个新的数据点时,这种情况都会继续发生,直到最终我的浏览器中有 20 个或更多图表,导致它在某个时候崩溃。

我尝试在socket.on 之外创建图表,即使使用与上面示例中完全相同的随机数据,它也没有显示任何内容。所以我假设我需要在 socket.on() 方法中创建图表并每两秒更新一次。 如何在不制作多个副本的情况下创建和更新图表?

这是我现在拥有的完整代码:

 socket.on('news', function (data) {

var o= JSON.parse(data);
awesome=o;
note3.push(awesome.valuee);
var t = -1
var n = 40,
    duration = 750
    data = d3.range(n).map(next);


   function next(){
    return {time: ++t, value: awesome.valuee }
    }

   var margin = {
    top: 6,
    right: 0,
    bottom: 20,
    left: 40
    },
    width = 560 - margin.right,
    height = 120 - margin.top - margin.bottom;

     var x = d3.scale.linear()
    .domain([t-n+1, t])
    .range([0, width]);

    var y = d3.time.scale()
    .range([height, 0])
    .domain([0, 400]);;

    var line = d3.svg.line()
    .interpolate("basis")
    .x(function (d, i) {return x(d.time);})
    .y(function (d, i) {return y(d.value);});

   var svg = d3.select("body").append("p").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .style("margin-left", -margin.left + "px")
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  svg.append("defs").append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height); 

   var xAxis = d3.svg.axis().scale(x).orient("bottom");
   var axis = svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(x.axis=xAxis); */

   var path = svg.append("g")
    .attr("clip-path", "url(#clip)")
    .append("path")
    .data([data])
    .attr("class", "line");

   tick();

   function tick() {

    // update the domains
    x.domain([t - n + 2 , t]);

    // push the accumulated count onto the back, and reset the count
    data.push(next());

    // redraw the line
    svg.select(".line")
        .attr("d", line)
        .attr("transform", null);

    // slide the x-axis left
    axis.transition()
        .duration(duration)
        .ease("linear")
        .call(x.axis);

    // slide the line left
    path.transition()
        .duration(duration)
        .ease("linear")
        .attr("transform", "translate(" + x(t-n) + ")")
        .each("end", tick);

    // pop the old data point off the front
    data.shift();

}

});

非常感谢。

【问题讨论】:

  • 在您引用的代码中,tick 函数正在更新图表。听起来您正在重新运行整个图表创建。当然,这是一个猜测,因为您没有显示任何代码...为您的问题创建一个可重现的示例,否则我们将无法提供帮助。
  • @Mark,我已经用相关代码编辑了这个问题。希望它可以帮助您理解问题。
  • 查看您的代码确认,您正在对每个套接字事件重新运行整个图表创建。您只需要在每个套接字事件上运行tick 函数即可。
  • @Mark 这很有道理。但是,如果你看next()函数,它使用了变量awesome,这基本上是我从服务器接收到的数据,我不能在socket.on方法之外使用它的值。

标签: javascript node.js sockets d3.js


【解决方案1】:

完全未经测试的代码,但您需要重组为如下内容:

var t = -1,
  n = 40,
  duration = 750,
  data = [];

var margin = {
    top: 6,
    right: 0,
    bottom: 20,
    left: 40
  },
  width = 560 - margin.right,
  height = 120 - margin.top - margin.bottom;

var x = d3.scale.linear()
  .domain([t - n + 1, t])
  .range([0, width]);

var y = d3.time.scale()
  .range([height, 0])
  .domain([0, 400]);;

var line = d3.svg.line()
  .interpolate("basis")
  .x(function(d, i) {
    return x(d.time);
  })
  .y(function(d, i) {
    return y(d.value);
  });

var svg = d3.select("body").append("p").append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .style("margin-left", -margin.left + "px")
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.append("defs").append("clipPath")
  .attr("id", "clip")
  .append("rect")
  .attr("width", width)
  .attr("height", height);

var xAxis = d3.svg.axis().scale(x).orient("bottom");
var axis = svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + height + ")")
  .call(x.axis = xAxis); * /

var path = svg.append("g")
  .attr("clip-path", "url(#clip)")
  .append("path")
  .data([data])
  .attr("class", "line");

function tick() {

  // update the domains
  x.domain([t - n + 2, t]);

  // redraw the line
  svg.select(".line")
    .attr("d", line)
    .attr("transform", null);

  // slide the x-axis left
  axis.transition()
    .duration(duration)
    .ease("linear")
    .call(x.axis);

  // slide the line left
  path.transition()
    .duration(duration)
    .ease("linear")
    .attr("transform", "translate(" + x(t - n) + ")");

  // pop the old data point off the front
  if (data.length > 40) data.shift();

}

socket.on('news', function(p) {

  p = JSON.parse(p);
  data.push({
    time: ++t,
    value: p.valuee
  })

  tick();

});

【讨论】:

  • 嘿,马克。它不打印任何东西,但我会尝试看看是否可以使用它作为基础。谢谢。
  • 更具体地说,变量data 不保存任何值。它总是空的。
  • 变量 data 在 socket.on 方法中具有值。但是,如果我们在该方法之外执行console.log,则同样不成立。所以图形没有形成,因为它在方法之外。
  • @manuelmourato,抱歉,data.shift(); 不应该在你达到一定数量的点之前触发,见上文。
  • 感谢您的耐心等待。我已经尝试使用您对 data.shift() 进行修改的代码,但它没有用。正如我昨天所说,这里的问题是 data 变量在 socket.on() 方法的 function(p) 内部具有我想要的值,但不在它之外。所以因为我们是在 socken.on 方法之外创建图,所以 data 变量总是为空的,无论等待 data.shift 多久,都无法绘制图。无论如何,你知道我可以解决这个问题吗?非常感谢。
【解决方案2】:

我已经使用 ws websocket 用 D3 实现了这个实时绘图,你可以看看,这是脚本,你也会有 html,但没有添加,此外,还有一个服务器,它服务于这个d3文件以及生成随机json,你可以创建这个服务器并检查它是否实时

<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
        </div>
            <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
            <script>
                 var ws = new WebSocket("ws://localhost:3000/");
                var count = 0, tempArr = [];
                ws.onmessage = function (message) {
                    if(count<10) {
                        tempArr.push(JSON.parse(message.data));
                        count++;
                        
                    }
                    init();
                }
                    
                    function init(){
                        var vis = d3.select("#visualisation"),
                        WIDTH = 1000,
                        HEIGHT = 500,

                        MARGINS = {
                            top: 20,
                            right: 20,
                            bottom: 20,
                            left: 50
                        },
                        xScale = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([2000, 2010]),
                        yScale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([134, 215]),
                        xAxis = d3.svg.axis()
                        .scale(xScale),

                        yAxis = d3.svg.axis()
                        .scale(yScale)
                        .orient("left"); 
                    
                    vis.append("svg:g")
                        .attr("class", "x axis")
                        .attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
                        .call(xAxis);
                    vis.append("svg:g")
                        .attr("class", "y axis")
                        .attr("transform", "translate(" + (MARGINS.left) + ",0)")
                        .call(yAxis);
                    

                    var lineGen = d3.svg.line()
                        .x(function(d) {
                            return xScale(d.year);
                        })
                        .y(function(d) {
                            return yScale(d.sale);
                        }) 
                        .interpolate("basis");
                    vis.append('svg:path')
                        //.enter()
                        .attr('d', lineGen(tempArr/*data*/))
                        //.attr('d', lineGen(obj.year))
                        .transition()
                        .duration(1000)
                        //.delay(2000)
                        .attr('stroke', 'green')
                        .attr('stroke-width', 2)
                        .attr('fill', 'none'); 
                        console.log(JSON.parse(message.data));

【讨论】:

    猜你喜欢
    • 2015-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-19
    • 1970-01-01
    • 2020-08-29
    • 1970-01-01
    • 2021-04-26
    相关资源
    最近更新 更多