D3.js语义缩放行为不端

时间:2013-11-23 07:06:13

标签: svg d3.js zoom

我一直在努力教自己D3.js,但我似乎无法通过语义缩放(缩放位置而不是形状)来为我工作。

我已阅读d3缩放文档here,并试图在功能上复制svg semantic zoom example code

这是我的代码:

var X, Y, circle, circles, h, i, j, svg, transform, w, zoom, _i, _j;

w = 1200;
h = 600;

circles = [];
for (j = _i = 0; _i <= 6; j = ++_i) {
  for (i = _j = 0; _j <= 12; i = ++_j) {
    circles.push({r: 25, cx: i * 50, cy: j * 50});
  }
}

X = d3.scale.linear()
    .domain([0, 1])
    .range([0, 1]);
Y = d3.scale.linear()
    .domain([0, 1])
    .range([0, 1]);

zoom = d3.behavior.zoom()
  .x(X)
  .y(Y)
  .on("zoom", function() {
      return circle.attr("transform", transform);
  });

transform = function(d) {
  return "translate(" + (X(d.cx)) + ", " + (Y(d.cy)) + ")";
};

svg = d3.select("body")
    .append("svg")
    .attr("width", w)
    .attr("height", h)
    .call(zoom)
    .append("g");

circle = svg.selectAll("circle")
    .data(circles)
    .enter().append("circle")
    .attr("r", function(d) {
        return d.r;
    }).attr("cx", function(d) {
        return d.cx;
    }).attr("cy", function(d) {
        return d.cy;
    }).attr("transform", transform);

jsfiddle的实时版。

这应该很简单。我正在创建在没有应用缩放时应该完全触摸的圆网格(距离为50像素,直径为50像素)。当我放大时,我希望圆圈分开,鼠标下的点保持静止。我希望使用鼠标滚轮可以使变焦平滑且线性。但是,圆圈​​应保持相同的大小,以便在我放大时它们停止触摸;我缩小时它们应该重叠。

相反,最初,圆圈的范围恰好是应有的两倍。当我放大和缩小时,中心点不在鼠标下方(并根据我的平移方式移动)。缩放是高度非线性的,在我缩小时渐近接近1(圆圈接触)的比例,并在我放大时快速加速。

这看起来很奇怪,我无法发现我的代码和语义缩放示例之间存在显着差异,这些示例按预期工作。我得出结论,我实际上并不了解D3变焦应该如何工作。有人可以把我排除在外吗?

2 个答案:

答案 0 :(得分:4)

您的代码非常接近正确:Working demo

使用比例来映射对象的位置

不是保存对象的确切位置,然后使用rangedomain设置为[0, 1]的比例,而是使用比例为您进行映射:

for (j = _i = 0; _i <= 6; j = ++_i) {
  for (i = _j = 0; _j <= 12; i = ++_j) {
    circles.push({
      r: 25,
      cx: i,
      cy: j,
      color: "#000"
    });
  }
}

X = d3.scale.linear()
    .domain([0, 6])
    .range([0, w]);
Y = d3.scale.linear()
    .domain([0, 12])
    .range([0, h]);

此处的更改是,现在D3 知道关于视口的宽高比以及它应该以什么比例转换比例,以便将点保持在{ {1}}鼠标下的静态。否则,它试图放大和缩小正方形,导致一种不和谐的体验。

答案 1 :(得分:3)

问题是圆圈在翻译时的初始位置。

Live code指出并修复了问题,并进行了一些其他修改:

var size = 600
var scale = 100

circles = []
for (var j = 0; j<6; j++) {
    for (var i = 0; i<6; i++) {
        circles.push({x: i*scale, y: j*scale })
    }
}

var X = d3.scale.linear()
    .domain([0,6*scale])
    .range([0,size])
var Y = d3.scale.linear()
    .domain([0,6*scale])
    .range([0,size])

function transform(d) {
    return "translate("+X(d.x)+", "+Y(d.y)+")"
}

var circle  /*fwd declaration*/

var zoom = d3.behavior.zoom()
    .x(X).y(Y)
    .on("zoom", function () {
        circle.attr("transform", transform)
    })

var svg = d3.select("body").append("svg")
    .attr("width", size).attr("height", size)
    .call(zoom)
    .append("g")

circle = svg.selectAll("circle")
    .data(circles)
    .enter().append("circle")
    .attr("r", 20)
/*the problem was this initial offset interfering with the
translation we were applying, resulting in very strange behavior*/
/*    .attr("cx", function (d) {return d.x})
    .attr("cy", function (d) {return d.y})*/
    .attr("transform", transform)

“scale”参数 应该什么都不做,但是如果添加这些注释行,它会影响初始位置并导致非直观效果。

最初的问题是:

  • 初始比例似乎比原本应该更大。

  • 缩小非常变化会产生明显的非线性渐近效应。

  • 缩小然后平移,然后放大后根本无法正常工作,图表在鼠标下滑动而不是保持固定。

所有这些都是初始立场的直接后果:

  • 初始距离显得更大,因为我们将原始位置加上缩放转换。

  • 非线性渐近效应是变焦平移距离逐渐变为零(如预期的那样),但最初应用的距离变为零,给出了非零变焦渐近线的外观。

  • 缩小时,D3认为缩小比用户更多(因为圆圈之间的距离更远),这意味着当应用平移时,D3跟踪的图像中心移动方式不同而不是用户期望的,这会导致缩放中心的影响不在鼠标之下。

您可以通过取消注释初始位置线并使用不同的scale参数应用相同的缩放操作来使用这些效果来理解它们。评论它们会导致圈子最初全部位于屏幕空间0,0,因此仅应用 缩放距离转换,这就是我们想要的。

支持musical_ut的回答,建议使用较小的世界空间坐标尺度,这不应该有任何区别,但这确实有助于我找出问题。