2

我试图在画布上绘制的 d3.js 地图的两种状态之间进行转换,但遇到了一点麻烦:转换并不是那么顺利。这与我使用 svg 的过渡完全不同。

我正在使用Irene Ros 在此 Bocoup 博客文章中描述的模式。

这是我的代码,我使用 d3.timer 和一个自定义元素来创建一个数据抽象,从中可以在画布上进行绘制。我怎样才能提高性能,使每个过渡延迟动画流畅而不是生涩的动作?

问题是这一行:path(topojson.feature(data, data.objects.counties.geometries[i]));

画布在每个 d3.timer() 间隔中重绘所有 2144 条县路径的时间太长。因此,每个间隔大约需要 1 秒,而要进行平滑过渡则需要 17 毫秒。

想法?

var width = 1600;

  d3.json("/data/counties_merged.json", function(error, data) {

    var projection = d3.geo.albersUsa();

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

    var tracts = topojson.feature(data, data.objects.counties);

    projection.scale(1).translate([0, 0]);

    var b = path.bounds(tracts);

    var whRatio = ((b[1][0] - b[0][0]) / (b[1][1] - b[0][1]));

    var height = (width / 2) * whRatio;

    var s = .98 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
      t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];

    projection.scale(s).translate(t);

    var canvas = d3.select("#quintiles")
            .append("canvas")
              .attr("class", 'canvasarea');

    var context = canvas.node().getContext("2d");

    var ratio = window.devicePixelRatio || 1;

    d3.select('.canvasarea')
                  .attr("width", width ).attr("height", height )
                  .style("width", ((width / ratio) ) + "px").style("height", ((height / ratio) ) + "px");


    //context.scale(ratio, ratio);

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

    if (error) throw error;


    var strokeWidth = 0.4;

    function drawCanvas(d,i) {

        // clear canvas
      context.fillStyle = "#fff";
      context.rect(0,0,canvas.attr("width"),canvas.attr("height"));
      context.fill();

      // select our dummy nodes and draw the data to canvas.
      var elements = dataContainer.selectAll("custom.county-path");
      elements.each(function(d,i) {

        context.beginPath();

          context.strokeStyle = '#333';
          context.fillStyle = d3.select(this).attr("fill");

          context.lineWidth = strokeWidth;
          path(topojson.feature(data, data.objects.counties.geometries[i]));

          context.stroke();
          context.fill(); 
        context.restore();

      })

    }

    // Create an in memory only element of type 'custom'
    var detachedContainer = document.createElement("custom");

    // Create a d3 selection for the detached container. We won't
    // actually be attaching it to the DOM.
    var dataContainer = d3.select(detachedContainer);

    var paths;

    function drawMap(cutoff) {

      paths = dataContainer.selectAll("custom.county-path")
        .data(data.objects.counties.geometries, function(d,i) { return d.id; });

        paths.transition().duration(0).duration(5).delay(function(d,i) { return i * 0.25; })
              .attr('fill',function(d,i) {
                return (d.quintile !== undefined) ? (d.quintile < cutoff) ? '#8A85FF' : '#efefef' : '#efefef';
              });

      paths
        .enter()
          .append('custom')
            .attr('class','county-path')
              .attr('fill',function(d,i) {
                return (d.quintile !== undefined) ? (d.quintile < cutoff) ? '#8A85FF' : '#efefef' : '#efefef';
              });
    }

    d3.timer(drawCanvas);

    drawMap(100);
    drawMap(30);

  });
4

0 回答 0