如何使用d3.js在GeoJSON路径上为对象设置动画?

时间:2013-07-22 18:13:46

标签: javascript d3.js

我正在使用D3.js从GeoJSON文件生成并呈现路径。这工作正常,但现在我想沿着那条路径动画一个物体。我知道如何使用D3和标准SVG来做到这一点:

  1. 创建转场并设置其持续时间
  2. 对于转换的每个帧,使用%complete来查找路径中的坐标
  3. 将对象移动到步骤2中找到的坐标
  4. 这很简单。但我遇到的问题是d3.geo.path()似乎没有像标准D3路径对象那样返回任何长度或位置数据(例如有用的getPointAtLength()方法)。所以我无法在路径上找到一个点的x,y坐标,比如25%。

    有没有办法获取这些数据? (或者有更好的方法,例如将d3.geo.path()转换为常规D3路径吗?)

    以下是我的代码的截断版本;这里有一个实例:http://jsfiddle.net/5m35J/4/

    json = {
        ... // snipped for brevity
    };
    
    // Draw a GeoJSON line on the map:
    
    map = $('#map');
    xy = d3.geo.mercator().scale(480000).translate([630700, 401100]);
    path = d3.geo.path().projection(xy);
    
    vis = d3.select("#map")
        .append("svg:svg")
        .attr("width", 960)
        .attr("height", 600);
    
    vis.append("svg:g")
        .attr("class", "route")
        .selectAll("path")
        .data(json.features)
        .enter()
        .append("svg:path")
        .attr("d", path)
        .attr("fill-opacity", 0.5)
        .attr("fill", "#fff")
        .attr("stroke", "#333");
    
    // Draw a red circle on the map:
    
    //len = 100; // how do I find the length of the path?
    origin_x = 100;
    origin_y = 100;
    
    group = vis.append("svg:g");
    
    circle = group.append("circle")
        .attr({
        r: 10,
        fill: '#f33',
        transform: function () {
            //var p = path.getPointAtLength(0)
            //return "translate(" + [p.x, p.y] + ")";
            return "translate("+ origin_x +","+ origin_y +")";
        }
    });
    
    // Animate the circle:
    
    duration = 5000;
    circle.transition()
        .duration(duration)
        .ease("linear")
        .attrTween("transform", function (d, i) {
        return function (t) {
            //var p = path.node().getPointAtLength(len*t) // d3.geo.path() doesn't provide a getPointAtLength() method!
            //return "translate("+[p.x,p.y]+")"
            var current_x = origin_x + origin_x * t;
            var current_y = origin_y + origin_y * t;            
            return "translate("+ current_x +","+ current_y +")";
        }
    });
    

1 个答案:

答案 0 :(得分:8)

好吧,我想通了,但我不完全确定我的解决方案是否是“正确”的方式。基本上,我使用D3来选择由d3.geo.path()对象创建的原始SVG元素。

请注意targetPathpathNodepathLength变量以及transform()attrTween()函数的更改:

// Draw a red circle on the map:

group = vis.append("svg:g");

var targetPath = d3.selectAll('.route')[0][0],
    pathNode = d3.select(targetPath).selectAll('path').node(),
    pathLength = pathNode.getTotalLength();

circle = group.append("circle")
    .attr({
    r: 10,
    fill: '#f33',
    transform: function () {
        var p = pathNode.getPointAtLength(0)
        return "translate(" + [p.x, p.y] + ")";
    }
});

// Animate the circle:

duration = 10000;
circle.transition()
    .duration(duration)
    .ease("linear")
    .attrTween("transform", function (d, i) {
    return function (t) {
        var p = pathNode.getPointAtLength(pathLength*t);
        return "translate(" + [p.x, p.y] + ")";
    }
});

现场示例在这里:http://jsfiddle.net/5m35J/6/