2012-11-21 171 views
6

当用户使用鼠标滚轮滚动出入时,您可以调整缩放速度吗?在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.zoom,其含有基质或坐标传递给在平移或缩放功能时,允许平移和图形的重新缩放。

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

这里很简单的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 + ")"); 
};  
+0

你知道了吗? – kishanio

+1

@ kishanio我提出了一个解决方案 –

回答

5

你需要用数学函数调节功能里面的规模当您选择功能重要的是,对于x = 0 y = 0你可以使用pow在这种情况下更容易Math.pow(d3.event.scale,.1)第二个参数在缩小时缩放更慢。

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

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

  • 好origialHeight *规模是newHeight

  • 的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/

1

我已经修改mbostock的drag+zoom example有一个4倍的变焦速度,并把它放在一个jsfiddle。我已经在下面解释了我的想法。这是我第一次尝试堆栈溢出的答案,请好。

正如RaúlMartín的回答中所解释的,您可以使用redraw()函数中的公式来更改缩放比率。您需要进行一些额外的步骤,以确保d3行为仍可以很好地与修改后的缩放比率一起使用。

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

如果更改缩放速度,d3 zoom.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 + ")"); 
}