d3 on click on circle pause and resume transition of marker along line

Viewed 1025

I would like help to correct my code to click the marker circle element to pause or resume transition of this element along the line. My code moves marker along a line and I can pause and resume this transition using on click on button element but I would like to be able to click on the marker circle itself, not the button. I have used various references including : http://www.nytimes.com/interactive/2013/09/25/sports/americas-cup-course.html http://jsfiddle.net/meetamit/UJuWX/3/ http://jsfiddle.net/Y62Hq/2/ D3 tween - pause and resume controls

I would ultimately like to be able animate a marker along a geo path, pause and resume this at points along the path and click through on these points.

this is my code so far:

<!DOCTYPE html>
<html lang="en">
    <head>
<meta charset="utf-8">
<title>Need help</title>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v3.min.js"></script>

<style type="text/css">

body{
    font-family:"Helvetica Neue", Helvetica, sans-serif;
    color: red;
}
button {
  position: absolute;
  top: 15px;
  left: 10px;
  background: #004276;
  padding-right: 26px;
  border-radius: 2px;
  cursor: pointer;
}
circle {
  fill: steelblue;
  stroke: pink;
  stroke-width: 3px;
}
.point{
    fill:green;

}
.line{
  fill: none;
  stroke: red;
  stroke-width: 4;
  stroke-dasharray: 4px,8px;
}
</style>
</head>
<body>

<button>Start</button>

<script>

    var w = 960,
        h = 500;

    var duration = 10000;

    var svg = d3.select("body").append("svg")
        .attr("width", w)
        .attr("height", h);

    var line = d3.line()
    .x(function(d){return (d)[0];})
    .y(function(d){return (d)[1];});

var data =
 [
  [480, 200],
  [580, 400],
  [680, 100],
  [780, 300],
  [180, 300],
  [280, 100],
  [380, 400]
];

//path to animate
var linepath = svg.append("path")
  .data([data])
  .attr("d", line)
    .attr('class', 'line')
    .attr("d", function(d){
        console.log(this);
        return line(d)
    });

var points = svg.selectAll("circle")
      .data(data)
        .enter()
        .append("circle")
      .attr("r", 7)
        .attr("transform", function(d) { return "translate(" + (d) + ")"; })
        .attr("class", "point");

var pauseValues = {
        lastTime: 0,
        currentTime: 0
        };
        var marker = svg.append("circle")
          .attr("r", 19)
          .attr("transform", "translate(" + (data[0]) + ")")
            .on('click', function(d,i){
                d3.select(this)
                .style("fill", "orange")
                .transition()
            });

function transition() {
  marker.transition()
        .duration(duration - (duration * pauseValues.lastTime))
        .attrTween("transform", translateAlong(linepath.node()))
        .on("end", function(){
        pauseValues = {
          lastT: 0,
          currentT: 0
        };
        transition()
      });
}

function translateAlong(path) {
  var l = path.getTotalLength();
  return function(d, i, a) {
    return function(t) {
      t += pauseValues.lastTime;
      var p = path.getPointAtLength(t * l);
      pauseValues.currentTime = t;
      return "translate(" + p.x + "," + p.y + ")";
    };
  };
}

d3.select('button').on('click',function(d,i){
  var self = d3.select(this);
  if (self.text() == "Pause"){
        self.text('Start');
        marker.transition()
      .duration(0);
        setTimeout(function(){
            pauseValues.lastTime = pauseValues.currentTime;
        }, 100);
  }else{
    self.text('Pause');
    transition();
  }
});

</script>
</body>
</html>
1 Answers

To check if the circle is moving in the click function use d3.active(), which...

... returns null if there is no such active transition on the specified node.

Like this:

.on('click', function(d, i) {
    if (d3.active(this)) {
        marker.transition();
        setTimeout(function() {
            pauseValues.lastTime = pauseValues.currentTime;
        }, 100);
    } else {
        transition();
    }
});

Here is your code with that change:

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v3.min.js"></script>

<style type="text/css">
  body {
    font-family: "Helvetica Neue", Helvetica, sans-serif;
    color: red;
  }
  
  button {
    position: absolute;
    top: 15px;
    left: 10px;
    background: #004276;
    padding-right: 26px;
    border-radius: 2px;
    cursor: pointer;
  }
  
  circle {
    fill: steelblue;
    stroke: pink;
    stroke-width: 3px;
  }
  
  .point {
    fill: green;
  }
  
  .line {
    fill: none;
    stroke: red;
    stroke-width: 4;
    stroke-dasharray: 4px, 8px;
  }

</style>

<body>

  <button>Start</button>

  <script>
    var w = 960,
      h = 500;

    var duration = 10000;

    var svg = d3.select("body").append("svg")
      .attr("width", w)
      .attr("height", h);

    var line = d3.line()
      .x(function(d) {
        return (d)[0];
      })
      .y(function(d) {
        return (d)[1];
      });

    var data = [
      [480, 200],
      [580, 400],
      [680, 100],
      [780, 300],
      [180, 300],
      [280, 100],
      [380, 400]
    ];

    //path to animate
    var linepath = svg.append("path")
      .data([data])
      .attr("d", line)
      .attr('class', 'line')
      .attr("d", function(d) {
        return line(d)
      });

    var points = svg.selectAll("circle")
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 7)
      .attr("transform", function(d) {
        return "translate(" + (d) + ")";
      })
      .attr("class", "point");

    var pauseValues = {
      lastTime: 0,
      currentTime: 0
    };
    var marker = svg.append("circle")
      .attr("r", 19)
      .attr("transform", "translate(" + (data[0]) + ")")
      .on('click', function(d, i) {
        if (d3.active(this)) {
          marker.transition();
          setTimeout(function() {
            pauseValues.lastTime = pauseValues.currentTime;
          }, 100);
        } else {
          transition();
        }
      });

    function transition() {
      marker.transition()
        .duration(duration - (duration * pauseValues.lastTime))
        .attrTween("transform", translateAlong(linepath.node()))
        .on("end", function() {
          pauseValues = {
            lastT: 0,
            currentT: 0
          };
          transition()
        });
    }

    function translateAlong(path) {
      var l = path.getTotalLength();
      return function(d, i, a) {
        return function(t) {
          t += pauseValues.lastTime;
          var p = path.getPointAtLength(t * l);
          pauseValues.currentTime = t;
          return "translate(" + p.x + "," + p.y + ")";
        };
      };
    }

    d3.select('button').on('click', function(d, i) {
      var self = d3.select(this);
      if (self.text() == "Pause") {
        self.text('Start');
        marker.transition()
          .duration(0);
        setTimeout(function() {
          pauseValues.lastTime = pauseValues.currentTime;
        }, 100);
      } else {
        self.text('Pause');
        transition();
      }
    });

  </script>
</body>

Related