简体   繁体   中英

D3 transition line happening twice

I am very new to D3, I am trying to make a simple live chart loosely based on a snippet from here: https://bost.ocks.org/mike/path/

I want to be able to add a number of lines to a moving live chart and have them be updated from a web socket (I know how to do that bit!!)

When I try and add a second line the chart doesn't update smoothly, I think the transition is being called twice. Any help gratefully received.

Code below but here's a fiddle https://jsfiddle.net/q8qgbj58/

var n = 243;
var random = d3.randomNormal(0, .2);

var duration = 500;
var now = new Date(Date.now() - duration);

var svg = d3.select("svg"),
    margin = {top: 20, right: 20, bottom: 20, left: 40},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var x = d3.scaleTime()
    .domain([now - (n - 2) * duration, now - duration])
    .range([0, width]);

var y = d3.scaleLinear()
    .domain([-1, 1])
    .range([height, 0]);

var lineNames = [];
var lines = {};
var data = {};

var line = d3.line()
    .curve(d3.curveBasis)
    .x(function(d, i) { return x(now - (n - 1 - i) * duration); })
    .y(function(d, i) { return y(d); });

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

var axis = g.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + y(0) + ")")
    .call(d3.axisBottom(x));

g.append("g")
    .attr("class", "axis axis--y")
    .call(d3.axisLeft(y));

createLine("one");
createLine("two");

d3.selectAll(".line")
    .transition()
        .duration(duration)
        .ease(d3.easeLinear)
        .on("start", tick);

function createLine(name) {
    lineNames.push(name);

    data[name] = d3.range(n).map(random);

    lines[name] = d3.line()
        .curve(d3.curveBasis)
        .x(function(d, i) { return x(now - (n - 1 - i) * duration); })
        .y(function(d, i) { return y(d); });

    g.append("g")
      .attr("clip-path", "url(#clip)")
        .append("path")
        .datum(data[name])
        .attr("class", "line");
}

function tick() {
    var index;
    var i;
    for (i = 0; i < lineNames.length; ++i) {
        index = lineNames[i];

        // Push a new data point onto the back.
        data[index].push(random());

        // Redraw the line.
        d3.select(this)
          .attr("d", lines[index])
          .attr("transform", null);

        // Pop the old data point off the front.
        data[index].shift();
    }

    now = new Date();
    x.domain([now - (n - 2) * duration, now - duration]);
    axis.transition()
        .duration(duration)
        .ease(d3.easeLinear)
        .call(d3.axisBottom(x));


    // Slide it to the left.
    d3.active(this)
      .attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
    .transition()
      .on("start", tick);
}

It looks like the issue was with updating the data for all the lines when one line was calling "tick". You will notice your example works fine with 1 line and is even more jerky with 3 lines. This is because of the for loop in the function tick. The data binding in D3 is very useful, but takes some time to get used to using.
The two major code changes I made were to make the line() a variable and removed the for loop from the tick function. An updated fiddle (I tried to just comment out the original code, so you can easily see the difference): https://jsfiddle.net/s07d2hs3/

var line = d3.line()
   .curve(d3.curveBasis)
   .x(function(d, i) { return x(now - (n - 1 - i) * duration); })
   .y(function(d, i) { return y(d); });

function tick() {
    var index;
    var i;
   // Push a new data point onto the back.
   this.__data__.push(random());
   // Redraw the line.
   d3.select(this)
      .attr("d", line)
      .attr("transform", null);

   // Pop the old data point off the front.
      this.__data__.shift();

    now = new Date();
    x.domain([now - (n - 2) * duration, now - duration]);
    axis.transition()
        .duration(duration)
        .ease(d3.easeLinear)
        .call(d3.axisBottom(x));

    // Slide it to the left.
    d3.active(this)
      .attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
    .transition()
      .on("start", tick);
}

Cleaned up fiddle: https://jsfiddle.net/Lr5dxgr0/2/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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