我一直在努力教自己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变焦应该如何工作。有人可以把我排除在外吗?
答案 0 :(得分:4)
您的代码非常接近正确:Working demo。
使用比例来映射对象的位置
不是保存对象的确切位置,然后使用range
和domain
设置为[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的回答,建议使用较小的世界空间坐标尺度,这不应该有任何区别,但这确实有助于我找出问题。