简体   繁体   中英

Curve d3 line animated using tweendash

Hi I am trying to curve the linestring like given in the fiddle http://jsfiddle.net/nj37gkgq/ . Line is an array of links having source and destination in the form of coordinates. This line is connecting two markers in d3.geo maps. How can I achieve this?

<!DOCTYPE html>
 <html lang='en'>

 <style type="text/css">



<body>


<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>

<div id="map"></div>

<script type="text/javascript">

    var width = 650,
    height = 600;

var projection = d3.geo.albersUsa()
    .scale(2500)
    .translate([1000, 360]);


var path = d3.geo.path()
    .projection(projection);

var svg = d3.select("#map").append("svg")
    .attr("width", width)
    .attr("height", height);

var coordinates = [
  [ -122.762, 40.801 ],
  [ -117.0978, 34.1178]
];


d3.json(url, function(ca) {

        svg.append("path")
            .datum(topojson.mesh(ca, ca.objects.subunits, function(a, b) { return a === b;}))
            .attr("d", path);

        //for stroke of lines
        var gradient = svg.append("svg:defs")
            .append("svg:linearGradient")
            .attr("id", "gradient")
            .attr("x1", "0%")
            .attr("y1", "0%")
            .attr("x2", "100%")
            .attr("y2", "100%")
            .attr("spreadMethod", "pad");

        gradient.append("svg:stop")
            .attr("offset", "0%")
            .attr("stop-color","darkblue")
            .attr("stop-opacity", 1);

        gradient.append("svg:stop")
            .attr("offset", "100%")
            .attr("stop-color","#85c1e9")
            .attr("stop-opacity", 1);

         var lF = d3.svg.line()
                      .interpolate("basis")
                      .x(function(d){ return d[0] })
                      .y(function(d){ return d[1] });

        //update
        var line = svg.append("path")
            .datum(coordinates)
            .attr("d", function(c){

                var d = {
                    source: projection(c[0]),
                    target: projection(c[1])
                },

                  points = [];

                  points.push(d.source);
                  points.push([(d.target[0] + d.source[0]) * .4, d.target[1]]);
                  points.push([(d.target[0] + d.source[0]) * .8, d.source[1]]);
                  points.push(d.target);
                  console.log(points);

                  return lF(points);
            })
            .attr("stroke-width", "2.5")
            .attr("stroke-linejoin", "round")
            .attr("stroke-linecap", "round")
            .style("stroke", "url(#gradient)");

        anim();

        function anim() {
            line.transition()
                .duration(2000)
                .attrTween("stroke-dasharray", function() {
                    var len = this.getTotalLength();
                    return function(t) {
                      return (d3.interpolateString("0," + len, len + ",0"))(t)
                    };
                })
              .each('end', anim);
        }

});


</script>

EDITS w/ Better Squiggle

For a more all purpose squiggle formula, I think two opposite arcs to the midpoint looks good (c is array of [[x1,y1], [x2,y2]] of long, lat):

function twoArc(c){
    var source = projection(c[0]),
        target = projection(c[1]),
        mid = [(source[0] + target[0])/2, (source[1] + target[1])/2],
        dx1 = mid[0] - source[0],
        dx2 = target[0] - mid[0],
        dy1 = mid[1] - source[1],
        dy2 = target[1] - mid[1],
        dr1 = Math.sqrt(dx1 * dx1 + dy1 * dy1),
        dr2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);

      var rv = "M";
      rv += source[0] + "," + source[1];
      rv += "A" + dr1 + "," + dr1 + " 0 0,1 ";
      rv += mid[0] + "," + mid[1];
      rv += "A" + dr2 + "," + dr2 + " 0 0,0 ";
      rv += target[0] + "," + target[1];

      return rv;
  }

Here's a running example with various "random" coordinates:

 <!DOCTYPE html> <meta charset="utf-8"> <style> path { fill: none; stroke: #000; stroke-linejoin: round; stroke-linecap: round; } </style> <body> <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> <script src="//d3js.org/topojson.v1.min.js"></script> <script> var width = 600, height = 350; var coordinates = [ [-118, 34], [-74, 40], [-86.75, 33.57], [-92.38, 35.22], [-84.87, 34.53], [-83.80, 41.60], [-96.07, 33.07], [-112.02, 41.18], [-111.0, 41.33] ]; var projection = d3.geo.albersUsa() .scale(700) .translate([width / 2, height / 2]); var path = d3.geo.path() .projection(projection); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); d3.json("https://rawgit.com/jgoodall/us-maps/master/topojson/state.json", function(error, us) { if (error) return console.error(error); svg.append("path") .datum(topojson.mesh(us)) .attr("d", path); var line = svg.append("path") .datum(twoRand()) .attr("d", twoArc) .style("stroke", "steelblue") .style("stroke-width", 3) .style("fill", "none"); anim(); function twoArc(c){ var source = projection(c[0]), target = projection(c[1]), mid = [(source[0] + target[0])/2, (source[1] + target[1])/2], dx1 = mid[0] - source[0], dx2 = target[0] - mid[0], dy1 = mid[1] - source[1], dy2 = target[1] - mid[1], dr1 = Math.sqrt(dx1 * dx1 + dy1 * dy1), dr2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); var rv = "M"; rv += source[0] + "," + source[1]; rv += "A" + dr1 + "," + dr1 + " 0 0,1 "; rv += mid[0] + "," + mid[1]; rv += "A" + dr2 + "," + dr2 + " 0 0,0 "; rv += target[0] + "," + target[1]; return rv; } function twoRand(){ var i1 = Math.floor(Math.random() * coordinates.length), i2 = Math.floor(Math.random() * coordinates.length); return [coordinates[i1], coordinates[i2]]; } function anim() { line.datum(twoRand()) .attr("d", twoArc); line.transition() .duration(2000) .attrTween("stroke-dasharray", function() { var len = this.getTotalLength(); return function(t) { return (d3.interpolateString("0," + len, len + ",0"))(t) }; }) .each('end', anim); } }); </script> 

EDITS with First Squiggle Attempt

Here's an example with a "squiggly" line. I generate it by inserting jittered points into an array and using a d3 line-fit interpolation:

 <!DOCTYPE html> <meta charset="utf-8"> <style> path { fill: none; stroke: #000; stroke-linejoin: round; stroke-linecap: round; } </style> <body> <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> <script src="//d3js.org/topojson.v1.min.js"></script> <script> var width = 600, height = 350; var coordinates = [ [-118, 34], //start point [-74, 40] //end point ]; var projection = d3.geo.albersUsa() .scale(700) .translate([width / 2, height / 2]); var path = d3.geo.path() .projection(projection); var lF = d3.svg.line() .interpolate("basis") .x(function(d){ return d[0] }) .y(function(d){ return d[1] }); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); d3.json("https://rawgit.com/jgoodall/us-maps/master/topojson/state.json", function(error, us) { if (error) return console.error(error); svg.append("path") .datum(topojson.mesh(us)) .attr("d", path); var line = svg.append("path") .datum(coordinates) .attr("d", function(c) { var d = { source: projection(c[0]), target: projection(c[1]) }, points = []; points.push(d.source); points.push([(d.target[0] - d.source[0]) * 0.4, d.target[1]]); points.push([(d.target[0] - d.source[0]) * 0.8, d.source[1]]); points.push(d.target); return lF(points); }) .style("stroke", "steelblue") .style("stroke-width", 3) .style("fill", "none"); anim(); function anim() { line.transition() .duration(2000) .attrTween("stroke-dasharray", function() { var len = this.getTotalLength(); return function(t) { return (d3.interpolateString("0," + len, len + ",0"))(t) }; }) .each('end', anim); } }); </script> 


With Single Arc

Coded this up before I saw your comment, but you seem to be stuck on not the dash tween but how to compute a path. I see know you want a curved path, but here's an example with a simple arc on a map (from LA to NY):

 <!DOCTYPE html> <meta charset="utf-8"> <style> path { fill: none; stroke: #000; stroke-linejoin: round; stroke-linecap: round; } </style> <body> <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> <script src="//d3js.org/topojson.v1.min.js"></script> <script> var width = 600, height = 350; var coordinates = [ [-118, 34], //start point [-74, 40] //end point ]; var projection = d3.geo.albersUsa() .scale(700) .translate([width / 2, height / 2]); var path = d3.geo.path() .projection(projection); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); d3.json("https://rawgit.com/jgoodall/us-maps/master/topojson/state.json", function(error, us) { if (error) return console.error(error); svg.append("path") .datum(topojson.mesh(us)) .attr("d", path); var line = svg.append("path") .datum(coordinates) .attr("d", function(c) { var d = { source: projection(c[0]), target: projection(c[1]) }; var dx = d.target[0] - d.source[0], dy = d.target[1] - d.source[1], dr = Math.sqrt(dx * dx + dy * dy); return "M" + d.source[0] + "," + d.source[1] + "A" + dr + "," + dr + " 0 0,1 " + d.target[0] + "," + d.target[1]; }) .style("stroke", "steelblue") .style("stroke-width", 3) .style("fill", "none"); anim(); function anim() { line.transition() .duration(2000) .attrTween("stroke-dasharray", function() { var len = this.getTotalLength(); return function(t) { return (d3.interpolateString("0," + len, len + ",0"))(t) }; }) .each('end', anim); } }); </script> 

Give me a few minutes and all see about a "snaked" line.

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