hawkeye
hawkeye

Reputation: 349

Circle Leaving Trails while transition in D3.js

I am drawing circles with every update using a smooth transition which goes from one location to another. Here is the code I am using...

function drawChart(newData) 
{

var circles = slider.selectAll(".dot")
  .data(newData);

  circles.enter()
  .append("circle")
  .merge(circles)
  .transition() // and apply changes to all of them
  .duration(1000)
  .ease(d3.easeLinear)
  .attr("class", "dot")
  .attr("r", 10.5)
  .attr("cx", function(d,i) {
    return Math.pow(d.open,i); })
  .attr("cy", function(d,i) { return Math.pow(i,5)+d.close; })
  .style("fill", function(d) { return color(d.class); });

  circles.exit()
 .remove();
}

This is how data is updated using the filterData function.

function filterData(dd){

  var newData = dataset.filter(function(d) {
      return d.date.getDate() == dd.getDate() && d.date.getMonth() == dd.getMonth();
  })

drawChart(newData)
}

This code shows the simple circle and transition, whereas I want to have the transition in a way circles are leaving trails while moving as in this picture. this picture.

Is there any way to do this? Any help would be appreciated.

Upvotes: 1

Views: 201

Answers (1)

Ruben Helsloot
Ruben Helsloot

Reputation: 13129

I made your starting positions a little easier to mock, the true calculations are in the .tween function. Note that I execute the function only a few times, otherwise you get a continuous flow of circles.

You can often find solutions like this by looking at similar problems. In this case, I based it on this answer, which led me to tween.

var svg = d3.select('svg');
var color = (v) => v;
var nTrails = 20;

function createTraceBall(x, y) {
  svg.append('circle')
    .classed('shadow', true)
    .attr('cx', x)
    .attr('cy', y)
    .attr('r', 10)
    .style('fill', 'grey')
    .style('opacity', 0.5)
    .transition()
    .duration(500)
    .ease(d3.easeLinear)
    .style('fill', 'lightgrey')
    .style('opacity', 0.1)
    .attr('r', 3)
    .remove();
}

function drawChart(newData) {
  var circles = svg.selectAll(".dot")
    .data(newData);

  circles.enter()
    .append("circle")
    .attr("cx", (d) => d.open.x)
    .attr("cy", (d) => d.open.y)
    .merge(circles)
    .transition() // and apply changes to all of them
    .duration(1000)
    .ease(d3.easeLinear)
    .tween("shadow", function(d) {
      var xRange = d.close.x - d.open.x;
      var yRange = d.close.y - d.open.y;
      var nextT = 0;
      return function(t) {
        // t is in [0, 1), and we only want to execute it nTrails times
        if(t > nextT) {
          nextT += 1 / nTrails;
          createTraceBall(
            d.open.x + xRange * t,
            d.open.y + yRange * t
          );
        }
      };
    })
    .attr("class", "dot")
    .attr("r", 10.5)
    .attr("cx", (d) => d.close.x)
    .attr("cy", (d) => d.close.y)
    .style("fill", function(d) { return color(d.class); });

    circles.exit()
   .remove();
}

drawChart([
  {open: {x: 20, y: 20}, close: {x: 150, y: 150}, class: 'red'},
  {open: {x: 150, y: 20}, close: {x: 20, y: 150}, class: 'blue'},
  {open: {x: 20, y: 20}, close: {x: 150, y: 20}, class: 'green'}
]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

Upvotes: 2

Related Questions