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