首页 文章

D3.js在canvas和svg中映射缩放行为

提问于
浏览
0

谢谢阅读 .

The Goal

我想比较从Mike Bostock的块到基于画布的系统的工作SVG点击缩放 . 我把工作的SVG放在顶部,底部放了一个画布 . 当用户点击上部SVG中的状态时,我希望下部画布元素“跟随”或模仿缩放和平移 . 例如,单击上部SVG中的明尼苏达州也会导致下部画布缩放并平移到明尼苏达州 .

The Problem

我的canvas元素在加载topojson后绘制得很好,但它没有动画效果 . 我想要它的动画 . 我相信这是因为我不完全理解缩放行为和基于路径的投影 .

http://jsfiddle.net/30w8nv4t/2/

function zoomed(d) {
  g.style("stroke-width", 1.5 / d3.event.scale + "px");
  g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");

  // the zTranslate and zScale variables appear to be ok,
  // but `d` is null. I'm not sure how to redraw.
  var zTranslate = zoom.translate();
  var zScale = zoom.scale();
  console.log(zTranslate, zScale, d);
  context.clearRect(0, 0, width, height);
  context.beginPath();
  canvasPath(d);
  context.stroke();
}

在这种情况下, d 显然为null,我无法重绘任何内容 . 我假设我的问题可能在 zoomed 方法或 clicked 函数中 .

The Reason

我正在使用这种并排的方法,因为我想了解路径,投影和缩放行为如何协同工作 . 我很欣赏canvas对SVG的表现如何,但缺乏交互性是令人生畏的 . 幸运的是,能够缩放和平移到任意几何体可以将我的问题减少一半 .

谢谢你的阅读 . 链接到JSFiddle是这篇文章的顶部 .

1 回答

  • 1

    canvas 绘图函数使用纬度/经度坐标的投影值,但您没有在 zoom 处理程序中更新 scalescaletranslate .

    获得您所追求的行为的一种方法是从 zoom 处理程序中的 svg 上的 transform 切换到 projection 上的 transform .

    我已经在这个更新的小提琴中做到了这一点:http://jsfiddle.net/30w8nv4t/7/

    不同之处是:

    • 更新 zoom 行为以使用 projection translatescale 作为默认值,并将 scaleExtent 值设置为基于 projection .
    var zoom = d3.behavior.zoom()
        .translate(projection.translate())
        .scale(projection.scale())
        .scaleExtent([projection.scale()/5, projection.scale()*5])
        .on("zoom", zoomed);
    
    • zoomed 函数更新为 translatescale projection ,然后重绘基于 svg 的路径 .
    function zoomed(d) {
      //g.style("stroke-width", 1.5 / d3.event.scale + "px");
      //g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
      projection.translate(d3.event.translate).scale(d3.event.scale);
      g.selectAll("path").attr("d", path);
    
      var zTranslate = zoom.translate();
      var zScale = zoom.scale();
      console.log(zTranslate, zScale, d);
      context.clearRect(0, 0, width, height);
      context.beginPath();
      canvasPath(states);
      context.stroke();
    }
    
    • 相应地更新 clicked 函数 .
    function clicked(d) {
        if (active.node() === this) {
            zoom.scale(500).translate([width/2, height/2]);
            active.classed("active", false);
            active = d3.select(null);
        } else {
            var centroid = path.centroid(d),
                translate = zoom.translate(),
                bounds = path.bounds(d),
                dx = bounds[1][0] - bounds[0][0],
                dy = bounds[1][1] - bounds[0][1],
                scale = .9/ Math.max(dx / width, dy / height);
    
            zoom.scale(scale * zoom.scale())
                      .translate([
                          translate[0] - centroid[0] * scale + width *  scale / 2, 
                          translate[1] - centroid[1] * scale + height * scale / 2]);
    
    
            active.classed("active", false);
            active = d3.select(this).classed("active", true);
        }
    
        zoom.event(svg);
    }
    

    这可能是更改的主要组件之一,因为 scaletranslate 应用于 zoom 行为,当它们出现时,它们必须由当前 zoom scale 缩放 . 然后 clicked 函数触发 zoomed 函数以重绘 svgcanvas 元素 .

    如您所见,您的 canvas 绘图代码是正确的 . 只是绘图代码使用 projection 来确定要根据 zoom 处理程序未更新的 projection 绘制的点的 xy 位置 .

    也可以为 canvas 单独设置 projection ,并在调用 canvas 重绘函数之前在 zoom 处理程序中更新它 . 我会把它留给读者练习!

相关问题