在D3中放大和缩小时如何更改翻译和缩放的速度(使用zoom.on和d3.event.translate,d3.event.zoom)?



当用户使用鼠标滚轮滚动时,你能调整缩放的速度吗?

我的理解是zoom.on(https://github.com/mbostock/d3/wiki/Zoom-Behavior#wiki-on)监听器产生两个事件d3.event.translate&d3.event.soom,其中包含矩阵或坐标,当传递给平移或缩放函数时,可以平移和重新缩放图形。

但是,我该如何加快速度,这样,如果用户稍微移动鼠标滚轮,她就会迅速放大或缩小?我有一个大的可视化,我想让用户可以用鼠标滚轮快速放大和缩小。我可以简单地修改/添加上述现有事件和函数的参数吗?还是必须创建自己的参数?我觉得上面的一些内容在理解方面不准确/不完整,所以如果是,请解释。

这里有一个非常简单的jsfiddle示例:http://jsfiddle.net/fiddler86/6jJe6/,以下代码相同:

var svg = d3.select("body").append("svg:svg")
.attr("width", 1000)
.attr("height", 2000)      
.append("svg:g")
.call(d3.behavior.zoom().on("zoom", redraw))
.append("svg:g");
svg.append("svg:rect")
.attr("width", 200)
.attr("height", 300)
.attr("fill", 'green');
function redraw() {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
};     

选择函数时,您需要用数学函数调整函数内部的比例。重要的是,对于x=0,y=0,您可以更容易地使用pow。在这种情况下,Math.pow(d3.event.scale,.1)第二个参数越小,缩放越慢。

使用一个非常复杂的功能不是一个好主意,因为浏览器会变慢。

当你有了新的比例,你需要重新计算翻译。您不会使问题复杂化,在SVG中,this.getBBox().height的实际高度可以,但这并不完全是因为您落后了一次迭代。你可以用(originalHeight * scale)计算新高度,用(originalHeight - (originalHeight * scale))/2计算平移

  • Well originalHeight*比例是新高度

  • originalHeight-newHeight是差异,您需要中心,你需要除以2,正方形的一半和下半部分。

  • 现在我们需要用宽度来做动作。它是相同的

代码:

var svg = d3.select("body").append("svg:svg")
.attr("width", 1000)
.attr("height", 2000)      
.append("svg:g")
.call(d3.behavior.zoom().on("zoom", redraw))
.append("svg:g");
svg.append("svg:rect")
.attr("width", 200)
.attr("height", 300)
.attr("fill", 'green');
function redraw() {
var velocity = 1/10;
var scale =  Math.pow(d3.event.scale,velocity);
var translateY = (300 - (300 * scale))/2;
var translateX = (200 - (200 * scale))/2;
svg.attr("transform", "translate(" + [translateX,translateY] + ")" + " scale(" +scale+ ")");            
};

注意,我把200和300硬编码,你可以使用一个属性,使用常量。。。

我创造了一个小提琴手:http://jsfiddle.net/t0j5b3e2/

我修改了mbostock的拖动+缩放示例,使其具有4倍的缩放速度,并将其放入jsfiddle中。我在下面解释了我的想法。这是我第一次尝试堆栈溢出的答案,请耐心等待。

正如Raúl Martín的回答中所解释的,您可以使用redraw()函数中的公式来更改缩放速率。你需要做一些额外的步骤来确保d3的行为在修改后的缩放率下仍然很好地工作。

以所选点(例如光标)为中心缩放
默认情况下,d3行为会将缩放集中在鼠标指针上,例如,如果鼠标指针位于图像的左上角,则它会放大到图像的左上方,而不是中心。为了获得这种效果,它缩放图像,然后更改图像的平移,使鼠标光标下的点保持在屏幕上的相同位置。这就是为什么当滚动鼠标滚轮时zoom.translate()的值会发生变化,即使图像看起来不像是在屏幕上移动。

如果更改缩放速度,d3zoom.translate()值将不再正确。要计算出正确的翻译,您需要知道以下信息(忽略数值):

var prev_translate = [100,100] // x, y translation of the image in last redraw
var prev_scale = 0.1           // Scale applied to the image last redraw
var new_scale = 0.4            // The new scale being applied
var zoom_cp = [150, 150]       // The zoom "center point" e.g. mouse pointer

计算应用于图像的new_translate的公式是:

new_translate[0] = zoom_cp[0] - (zoom_cp[0] - prev_translate[0]) 
* new_scale / prev_scale;
new_translate[1] = zoom_cp[1] - (zoom_cp[1] - prev_translate[1]) 
* new_scale / prev_scale;

您可以将此应用于图像以及您的新比例,使用:

svg.attr("transform", "translate(" + new_translate + ")scale(" + new_scale + ")");

然后,您必须更新prev_scale = new_scaleprev_translate = new_translate,为redraw()的下一次迭代做好准备

平移而不缩放

d3缩放行为允许您通过单击和拖动进行平移,而无需缩放。如果单击并拖动,则zoom.scale()保持不变,但zoom.translate()发生变化。即使修改了缩放速度,新的zoom.translate()值仍然正确。但是,您需要知道何时使用此zoom.translate()值,以及何时使用为放大中心点而计算的平移值。

您可以通过查看prev_scale是否与new scale相同来判断是平移还是缩放。如果这两个值相同,则知道正在进行平移,可以使用new_translate = zoom.translate()移动图像。否则,您知道正在进行缩放,并且可以如上所述计算new_translate值。我通过向zoomstart事件添加一个函数来实现这一点。

var zoom_type = "?";
var scale_grad = 4; // Zoom speed multiple
var intercept = 1 * (1 - scale_grad)
var svg = d3.select("body").append("svg:svg")
.attr("width", 1000)
.attr("height", 2000)      
.append("svg:g")
.call(d3.behavior.zoom()
.on("zoom", redraw)
.on("zoomstart", zoomstarted))
.append("svg:g");
function zoomstarted() {
zoom_type = "?";
}
function redraw() {
var scale = d3.event.scale;
// Use a linear scale, don't let it go below the minimum scale
// extent
var new_scale = Math.max(scale_grad * scale + intercept, 
scale_extent[0]);
// If hit the minimum scale extent then stop d3 zoom from 
// going any further
if (new_scale == scale_extent[0]) {
zoom.scale((scale_extent[0] - intercept) / scale_grad);   
}
// Set up zoom_type if have just started
// If the scale hasn't changed then a pure translation is
// taking place, otherwise it is a scale
if (zoom_type == "?") {
if (new_scale == prev_scale) {
zoom_type = "translate"
} else {
zoom_type = "scale"
}
}
// zoom_cp is the static point during the zoom, set as 
// mouse pointer position (you need to define a listener to track)
var new_translate = [0, 0];
zoom_cp = [mouse_x, mouse_y];
// If the event is a translate just apply d3 translate
// Otherwise calculate what translation is required to 
// keep the zoom center point static
if (zoom_type == "translate") {
new_translate = d3.event.translate
} else if (zoom_type == "scale") {
new_translate[0] = zoom_cp[0]
- (zoom_cp[0] - prev_translate[0]) * new_scale / prev_scale;
new_translate[1] = zoom_cp[1] 
- (zoom_cp[1] - prev_translate[1]) * new_scale / prev_scale;
}
// Update the variables that track the last iteration of the 
// zoom
prev_translate = new_translate;
prev_scale = new_scale;
zoom.translate(new_translate);
// Apply scale and translate behaviour
svg.attr("transform", "translate(" + new_translate + 
")scale(" + new_scale + ")");
}

最新更新