sjaikumar sjaikumar - 11 days ago 6
Javascript Question

Specify startpoint to interpolate a circle on an arc when clicked on by the user

How to provide the starting point of an arc for interpolation along the path of the arc through a circle. I have a world map with several arcs displayed on it. I wish to interpolate the movement of a circle on that arc that has been selected by the user using the .on('click') event. I wish to know how do I identify the startPoint of the arc in question.

Specifically, I am not able to understand what parameters to provide in ".attr("transform", "translate(" + startPoint + ")")" attribute of the circle to enable the circle to start from the starting position of the arc.

At present, it passes the entire path and I I receive the following error "d3.v3.min.js:1 Error: attribute transform: Expected number, "translate(M1051.5549785289…"."
Athough, surprisingly, the circle marker appears on the screen and interpolates along the first arc that has been drawn. However, I wish to change this interpolation to an arc that has been clicked by the user. In other words, how do I feed a new startPoint to the circle marker every time the user clicks on a different arc and subsequent interpolation of the sameenter image description here

var path3 = arcGroup.selectAll(".arc"),
startPoint = pathStartPoint(path3)

var marker = arcGroup.append("circle")
marker.attr("r", 7)
.attr("transform", "translate(" + startPoint + ")")

transition();
function pathStartPoint(path) {
var d = path.attr("d")
console.log(path)
dsplitted = d.split(" ");
return dsplitted[0].split(",");
}

function transition() {
marker.transition()
.duration(7500)
.attrTween("transform", translateAlong(path3.node()))
.each("end", transition);// infinite loop
}

function translateAlong(path) {
var l = path.getTotalLength();
return function(i) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";//Move marker
}
}


}

Answer

As @altocumulus states in the their comment, getPointAtLength doesn't need a starting point. It takes as an argument a distance from 0 to path length where 0 is the starting point. Here's a quick example, click on any path below:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <script>
    var w = 400,
      h = 400;

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

    var data = []
    for (var i = 0; i < 5; i++) {
      data.push({
        x0: Math.random() * w,
        y0: Math.random() * h,
        x1: Math.random() * w,
        y1: Math.random() * h
      });
    }

    var marker = svg.append("circle")
      .attr("r", 20)
      .style("fill", "steelblue")
      .style("opacity", 0);

    svg.selectAll("path")
      .data(data)
      .enter()
      .append("path")
      .attr("d", function(d) {
        var dx = d.x1 - d.x0,
          dy = d.y1 - d.y0,
          dr = Math.sqrt(dx * dx + dy * dy);
        return "M" + d.x0 + "," + d.y0 + "A" + dr + "," + dr +
          " 0 0,1 " + d.x1 + "," + d.y1;
      })
      .style("stroke", function(d, i) {
        return d3.schemeCategory10[i];
      })
      .style("stroke-width", "10px")
      .style("fill", "none")
      .on("click", function(d){
          marker
            .style("opacity", 1)
            .transition()
            .duration(1000)
            .attrTween("transform", translateAlong(this))
            .on("end", function(d) {
              marker.style("opacity", 0);
            });
      });

    function translateAlong(path) {
      var l = path.getTotalLength();
      return function(i) {
        return function(t) {
          var p = path.getPointAtLength(t * l);
          return "translate(" + p.x + "," + p.y + ")"; //Move marker
        }
      }
    }
  </script>
</body>

</html>