【问题标题】:d3 real time graph syncing issuesd3 实时图形同步问题
【发布时间】:2019-03-31 22:25:06
【问题描述】:

我花了几天时间尝试创建一个异步附加数据的实时时间线。如果不每次都破坏其他东西,似乎不可能让事情顺利进行。

我查看了几个示例,但似乎没有一个与我的情况相符。也就是说,大多数实时示例要么依赖数据每次将时间线递增一步,要么都假设数据以固定间隔连续出现。

我遇到的问题:

  1. 点滑动得很好。但是,如果我切换选项卡并切换回来,它们会从离开的位置继续,因此与 x 轴上的当前刻度不匹配
  2. 时间线中的滴答声时不时会出现一些奇怪的过渡,看起来像是在洗牌。改组后,实际点与时间线不同步。

这是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>

【问题讨论】:

    标签: javascript d3.js data-visualization real-time


    【解决方案1】:

    只是一些更新。我的解决方法是在很小的间隔(即 20 毫秒)内重新绘制所有内容。

    这解决了所有同步问题,但不确定性能是否会有所不同。

    类似的东西

    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)
    

    REFRESH_INTERVAL 设置为 20 毫秒,看起来与进行转换几乎相同。上面的任何东西都开始看起来不稳定,但至少比以前更准确。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-24
      • 2019-12-30
      • 1970-01-01
      相关资源
      最近更新 更多