honzajde
honzajde

Reputation: 2398

d3.js dynamic time series like shifting effect

I am trying to achieve time series shifting effect with circles like in this classical example. However, look at jsFiddle and see that I was not able to achieve a shifting effect.

Instead, something seems to be wrong with the indexes of the dataset. Looks like indexes are stuck with their (should be previous) values!?

Can anyone help, pleas?

Here is the js code in the jsFiddle.

<body>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <svg width="800" height="500"></svg>
    <script type="text/javascript">
        var x = d3.scale.linear()
            .domain([0, 1])
            .range([0, 800]);
        var data = [];
        setInterval(function(){
            if (data.length < 3) {
                data.push({value:Math.random(), date:new Date()});
            }
            else {
                data.shift();
                data.push({value:Math.random(), date:new Date()});
            }
            draw();
        }, 1000);
        function draw() {
            var svg = d3.select('svg');
            var circles = svg.selectAll('circle')
                    .data(data, function(d, i) { return d.value; });
            circles.attr('fill', 'orange');
            circles.exit()
                    .attr('fill', 'black')
                    .transition()
                    .duration(500)
                    .remove();
            circles.enter()
                    .append('circle')
                    .attr('fill', 'red')
                    .attr('r', 40)
                    .attr('cx', function(d, i) {
                        console.log("i: " + i);
                        return i*100 + 50;
                    })
                    .attr('cy', 50)
                    .transition()
                    .duration(500)
                    .attr('fill', 'blue');
            circles.style('stroke', 'black');
        }
    </script>
</body>

Upvotes: 1

Views: 707

Answers (2)

honzajde
honzajde

Reputation: 2398

So I found there is a free book on the internet, that shows how to do shifting effect. I suspect, that the classical example no longer works, since the author of the book uses ordinal scale and remembers greatest key value (see lastKeyValue) within dataset to make work both operations add and remove. Also updating axis helps....

You can see the whole example here on github.

I would still like to know if this is is the sort of canonical way to do this... I understand why it works in the book but don't understand why it doesn't work the way I did it in my example.

UPDATE:

Thanks to @wilbur I think I get it now. What I was confused about is that people say that d3 has databinding - now I know that d3 has no databinding - it only gives you diff and the rest is up to developer (Is this React style?).

Another thing is somebody should say how those indexes in the sets enter and exit are defined. Rather then describing it by words, I created another fiddle in that look into console and that says something about it. Btw. this same fiddle leaks memory (at least in my Chrome).

Thanks @wilbur. Now I am feeling happy and stupid at the same time:) And I am going to explore the leak.

Upvotes: 0

wpercy
wpercy

Reputation: 10090

The reason it isn't working in your example is because the new value is always enter()ing at index 2 and you don't have a .transition() for the circles, you just have transitions on exit and enter, which means that nothing will happen when the data changes (in this case, the index is updated). Here's a working jsfiddle of what I think you're trying to do. I changed your .shift() and .push()'s to .pop() and .unshift()'s to get the circles to move from left to right instead of right to left because it looked like that was what you were attempting. I also decreased the width of your svg so the exit()ing circle is no longer visible when .remove() is called on it.

NEW JSFIDDLE: jsfiddle

Here's the updated js code:

var data = [];

setInterval(function(){
    if (data.length < 3) {
        data.push(Math.random());
    }
    else {
        data.shift();
        data.push(Math.random());
    }
    draw();
}, 1000);

function draw() {
    var svg = d3.select('svg');
    var circles = svg.selectAll('circle')
            .data(data, function(d, i) { return d; });

    circles.attr('fill', 'orange');
    circles.style('stroke', 'black');

    circles.enter()
            .append('circle')
            .attr('fill', 'red')
            .style('stroke', 'black')
            .attr('r', 40)
            .attr('cy', 50)
            .attr('cx', function(d, i) { return (data.length)*100 + 50; })
        .transition()
            .duration(500)
            .attr('cx', function(d, i) { return i*100 + 50; });

    circles.exit()
            .attr('fill', 'black')
        .transition()
            .duration(500)
            .attr('cx', function(d, i) { return (i - 1)*100 + 50; })
            .remove();

    circles.transition()
            .duration(500)
            .attr('cx', function(d, i) { return i*100 + 50; });
}

Upvotes: 1

Related Questions