How to change the translation and scaling speed when scaling and decreasing in D3 (using zoom.on and d3.event.translate, d3.event.zoom)?

Can you adjust the zoom speed when the user scrolls and zooms out with the mouse?

I understand that the zoom.on listener ( https://github.com/mbostock/d3/wiki/Zoom-Behavior#wiki-on ) creates two events d3.event.translate and d3.event. zoom, which contain matrices or coordinates that are passed to the translation or scaling functions, allows panning and zooming of graphics.

But how can I speed it up, so if the user moves the mouse a bit, it will quickly zoom in or out? I have a great visualization that I want users to be able to quickly zoom in and out with the mouse. Can I just change / add arguments to the above events and functions or do I need to create my own? I feel that some of the above are inaccurate / heterogeneous in terms of understanding, so please explain if this is the case.

A very simple jsfiddle example: http://jsfiddle.net/fiddler86/6jJe6/ , with the identical code below:

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 + ")"); }; 
+6
source share
2 answers

You need to adjust the scale inside the function using a mathematical function, when you select a function that is important for x = 0. y = 0, in this case you can use pow, Math.pow(d3.event.scale,.1) , second the option zooms in more slowly when less.

It is not a good idea to use a very complex function, because the browser will be slow.

When you have a new scale, you need to recount the translation. You do not complicate the problem, in SVG you have the actual height with this.getBBox().height Height, this is normal, but this is not entirely true because you are one iteration behind. You can calculate the new height using (originalHeight * scale) and translate using (originalHeight - (originalHeight * scale))/2

  • Well, the original scale * is newHeight

  • Original Height - newHeight is the difference and you want the center you need to divide by 2, half the square and half lower.

  • Now we need to perform the action with the width. It is the same

The code:

  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+ ")"); }; 

Please note that I put 200 and 300 hardcoded, you can use the property, use a constant ...

I created a violinist: http://jsfiddle.net/t0j5b3e2/

+5
source

I modified the mbostock drag & zoom example to have a 4x zoom speed and put it in jsfiddle . I explained my thinking below. This is my first attempt to respond to a stack overflow, please be beautiful.

As explained in Raoul Martin's answer, you can use the formula in the redraw() function to change the zoom speed. You need to take a few extra steps to make sure that the d3 behavior still works well with the modified zoom speed.

Scale centered at the selected point (e.g., cursor)
By default, the behavior of d3 focuses the zoom on the mouse pointer, for example. if you have a mouse cursor in the upper left corner of the image, then it is enlarged in the upper left corner, and not in the center of the image. To get this effect, it scales the image, and then changes the translation of the image so that the point under the mouse cursor remains in the same place on the screen. Therefore, the value of zoom.translate() changes when you scroll the mouse wheel, even if the image does not look like moving around the screen.

If you change the zoom speed, the d3 zoom.translate() values ​​are no longer true. To work out the correct translation, you need to know the following information (ignore the numerical values):

 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" eg mouse pointer 

The formula for developing new_translate for applying to an image is:

 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; 

You can apply this to the image along with your new scale:

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

Then you need to update prev_scale = new_scale and prev_translate = new_translate , ready for the next iteration of redraw()

Pan without scaling

The d3 zoom mode allows you to pan without zooming by clicking and dragging. If you click and drag, then zoom.scale() will remain the same, but zoom.translate() will change. The new zoom.translate() value is still preserved even after changing the zoom speed. However, you need to know when to use this zoom.translate() value and when to use the translation value that you calculate to scale at the center point.

You can decide whether panorama or zooming occurs if you see that prev_scale same as new scale . If both values ​​are identical, you know that the pan takes place, and you can use new_translate = zoom.translate() to move the image. Otherwise, you know that scaling is happening, and you can calculate the new_translate value as described above. I do this by adding a function to the zoomstart event.

 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 + ")"); } 
+1
source

All Articles