简体   繁体   English

d3实时图同步问题

[英]d3 real time graph syncing issues

I've spent several days trying to create a real-time timeline where data is appended asynchronously. 我花了几天时间尝试创建一个实时时间轴,其中数据是异步附加的。 It seems impossible to get things working smoothly without breaking something else each time. 似乎不可能在不破坏其他东西的情况下让事情顺利进行。

I had a look at several examples but none of them seem to match my case. 我看了几个例子,但似乎没有一个与我的情况相符。 Namely, most of the real-time examples either rely on the data to increment the timeline by a step each time, or either they all assume that the data comes continuously in regular intervals. 也就是说,大多数实时示例要么依赖于数据来每次将时间线递增一步,要么它们都假设数据以规则的间隔连续出现。

Issues I'm having: 我遇到的问题:

  1. Points slide nicely. 积分滑得很好。 However if I switch a tab and switch back they continue from where they left, and thus not matching the current ticks in the x axis 但是,如果我切换一个标签并切换回来,它们将从它们离开的位置继续,因此不匹配x轴上的当前标记
  2. The ticks in the timeline every now and then get some weird transition that looks like shuffling. 时间轴中的滴答片时不时地得到一些看起来像洗牌的奇怪过渡。 After the shuffling the actual points are out of sync with the timeline. 在洗牌之后,实际点与时间线不同步。

Here's a fiddle 这是一个小提琴

<!doctype html><html lang="en">
<head><script src="https://d3js.org/d3.v5.min.js"></script></head>
<body><script>

    const LENGTH = 10 // in seconds
    const TICKS = 10 // num of ticks in time axis
    const HEIGHT = 240
    const WIDTH = 950
    const MARGIN_LEFT = 40
    const MARGIN_TOP = 40
    var datapoints = []

    // Create root element + background rect
    svg = d3.select('body').append('svg')
      .attr('width', WIDTH)
      .attr('height', HEIGHT)
    svg.append('rect')
      .attr('width', '100%')
      .attr('height', '100%')
      .attr('fill', 'rgba(59, 58, 52, 0.8)')
    $graphs = svg.append('g')
    $slidables = $graphs.append('g')

    // We use two scalers for x. This solves the issue with the axis being out
    // of sync
    scaleX = d3.scaleTime().range([MARGIN_LEFT, WIDTH-MARGIN_LEFT])
    scaleY = d3.scaleLinear().range([MARGIN_TOP, HEIGHT-MARGIN_TOP])
    updateTimeScale()

    // -------------------------------------------------------------------------

    function logDate(date){
      console.log(date.toTimeString().split(' GMT')[0] + ' ' + date.getMilliseconds() + 'ms')
    }

    function logPoint(point){
      const date = point[0]
      console.log(
          date.toTimeString().split(' GMT')[0] + ' ' + date.getMilliseconds() + 'ms, ',
          point[1]
      )
    }

    function oneSecondAgo(){
      d = new Date()
      d.setSeconds(d.getSeconds() - 1)
      return d
    }

    function leftDate(){
      d = new Date()
      d.setSeconds(d.getSeconds() - LENGTH)
      return d
    }

    function tickDist(){
        return scaleX(new Date()) - scaleX(oneSecondAgo())
    }


    // -------------------------------- Init -----------------------------------

    /* Resets timescale to the current time */
    function updateTimeScale(){
      right = new Date()
      left = new Date()
      right.setSeconds(right.getSeconds())
      left.setSeconds(right.getSeconds()-LENGTH)
      scaleX.domain([left, right])
    }

    function init(){
      // Setup axis
      xaxis = d3.axisBottom(scaleX).ticks(TICKS)
      $xaxis = svg.append('g')
        .attr('transform', 'translate(0, ' + (HEIGHT - MARGIN_TOP) + ')')
        .call(xaxis)
      yaxis = d3.axisLeft(scaleY)
      $yaxis = svg.append('g')
        .attr('transform', 'translate(' + MARGIN_LEFT + ', 0)')
        .call(yaxis)

      // Garbage collect old points every second
      setInterval(function(){
          while (datapoints.length > 0 && scaleX(datapoints[0][0]) <= MARGIN_LEFT){
            datapoints.shift()
          }
          $slidables.selectAll('circle')
            .data(datapoints, d=>d)
            .exit()
            .remove()
      }, 1000)

      // Slide axis at interval
      function tick(){
        right = new Date()
        left = new Date()
        right.setSeconds(right.getSeconds()+1)
        left.setSeconds(right.getSeconds()-LENGTH)
        scaleX.domain([left, right])
        $xaxis.transition()
          .ease(d3.easeLinear)
          .duration(new Date() - oneSecondAgo())
          .call(xaxis)
      }
      tick()
      setInterval(tick, 1000)
    }

    // ------------------------------ Update -----------------------------------

    /* Update graph with points

    We always set right edge to current time
    */
    function update(points){
      datapoints = datapoints.concat(points)
      logPoint(points[0])
      updateTimeScale()

      // Add new points, transition until left edge and then remove
      $slidablesEnter = $slidables.selectAll('circle')
        .data(datapoints, d=>d)
        .enter()
      $slidablesEnter
        .append("circle")
        .style("fill", "rgb(74, 255, 0)")
        .attr("r", 2)
        .attr("cx", p=>scaleX(p[0]))  // put it at right
        .attr("cy", p=>scaleY(p[1]))
      .transition()
        .duration(function(p){
          remainingTime = p[0] - leftDate()
          return remainingTime
        })
        .ease(d3.easeLinear)
        .attr("cx", p => MARGIN_LEFT)
        .remove()
    }

    // Start everything with two random datapoints
    init()
    d1 = new Date()
    d2 = new Date()
    d2.setMilliseconds(d2.getMilliseconds()-1500)
    update([[d1, Math.random()]])
    update([[d2, Math.random()]])

</script></body></html>

Just some updates. 只是一些更新。 My workaround is to repaint everything on a tiny interval (ie 20ms). 我的解决方法是在很短的间隔(即20毫秒)重绘所有内容。

This solves all the syncing issues but not sure if there will be a difference on performance. 这解决了所有同步问题,但不确定性能是否会有所不同。

Something like this 像这样的东西

function tick(){
        // Redraw all elements
        scaleX.domain([now(-TIMELINE_LENGTH_MS), now()])
        $slidables.selectAll('circle')
          .data(datapoints, d=>d)
          .attr("cx", p =>  scaleX(p[0]))
          .transition()
          .duration(0)
}
setInterval(tick, REFRESH_INTERVAL)

Setting REFRESH_INTERVAL to 20ms, looks pretty much the same as having a transition. REFRESH_INTERVAL设置为20ms,看起来与进行转换非常相似。 Anything above starts looking chopppy, but at least is more accurate than before. 上面的任何东西开始看起来很狡猾,但至少比以前更准确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM