9

当用户使用鼠标滚轮滚入和滚出时,您可以调整缩放速度吗?

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

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

这里非常简单的 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 + ")");
};     
4

2 回答 2

8

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

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

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

  • 那么 origialHeight*scale 是 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/

于 2015-06-18T21:28:53.043 回答
3

我修改了 mbostock 的drag+zoom 示例,使其具有 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 + ")");
}
于 2015-06-23T02:10:35.210 回答