score:3
I've modified mbostock's drag+zoom example to have a 4x zoom speed and put it in a jsfiddle. I've explained my thinking below. This is my first attempt at a stack overflow answer, please be nice.
As explained in Raúl Martín's answer you can use a formula within the redraw()
function to change the rate of zoom. You need to make some extra steps to make sure that the d3 behaviour still works nicely with the modified zoom rate.
Zoom centered on a chosen point (e.g. cursor)
By default d3 behaviour focuses the zoom on the mouse pointer, e.g. if you have the mouse pointer at the top left of the image it zooms in on the top left rather than the center of the image. To get this effect it scales the image and then also changes the translation of the image so that the point under the mouse cursor stays at the same location on the screen. This is why the value of zoom.translate()
changes when you scroll the mousewheel even though the image doesn't look like it is moving across the screen.
If you change the zoom speed the d3 zoom.translate()
values are no longer correct. To work out the correct translation you need to know the following information (ignore the numeric 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" e.g. mouse pointer
The formula to work out the new_translate
to apply to the image is then:
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 with:
svg.attr("transform", "translate(" + new_translate + ")scale(" + new_scale + ")");
You'll then have to update prev_scale = new_scale
and prev_translate = new_translate
ready for the next iteration of redraw()
Pan without scaling
The d3 zoom behaviour allows you to pan without scaling by clicking and dragging. If you click and drag then zoom.scale()
stays the same but zoom.translate()
changes. The new zoom.translate()
value is still correct even after you have modified the zoom speed. However, you need to know when to use this zoom.translate()
value and when to use the translate value that you calculate for zooming in on a center point.
You can work out whether a pan or a zoom is happening by looking at whether prev_scale
is the same as new scale
. If the two values are identical you know a pan is taking place and you can use new_translate = zoom.translate()
to move the image. Otherwise, you know that a zoom is taking place 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 + ")");
}
score:8
You need adjust the scale inside the function with a mathematical function when you select the function the important is that for x=0 the y=0 you can use pow is easier in this case Math.pow(d3.event.scale,.1)
the second parameter does the zoom more slowly when is smaller.
It´s not a good idea use a very complicated function because the browser will turn slow.
When you have the new scale, you need recalculate the translation. You don´t complicate the problem, in SVG you have the actual height with this.getBBox().height
this ok, but it is not exactly because you are one iteration behind. You could calculate the new height with (originalHeight * scale)
and the translate with (originalHeight - (originalHeight * scale))/2
Well origialHeight*scale is the newHeight
The originalHeight - newHeight is the difference, and you want the center, you need divide for 2, the half part of the square and the half part below.
Now we need do 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+ ")");
};
Note that I put the 200 and 300 hardcoded, you can use a property, use constant...
I created a fiddler: http://jsfiddle.net/t0j5b3e2/
Source: stackoverflow.com
Related Query
- How to change speed of translate and scale when zooming in and out in D3 (using zoom.on & d3.event.translate, d3.event.zoom)?
- How do you translate HTML elements along with SVG elements when zooming and panning with D3
- D3.js: How to keep marker and text on same position when zooming in and out
- How to keep current zoom scale and translate level when re-rendering graph
- D3 - How to get correct scale and translate origin after manual zoom and pan to country path
- When a d3.behavior.zoom event is triggered programatically, how do you set inital values for translate and scale?
- How to select and deselect a svg line and change its color on selection and deselection using d3.js and javascript , jquery?
- D3 word-cloud: how to auto size the font so words will not run out , and to scale words to all the screen?
- How to center and scale using geoPath
- How to change table cell color based on numeric value from .csv report AND when tables are dynamically created?
- How to keep the circle, stroke and font size when zooming in d3.js V4 layout?
- How to scale and translate together?
- How to fix d3 parsing error when using function and return value to set "d"?
- How to show D3 chart using Bootstrap modal popup when click on button and download it as image format
- How to change json file in d3.js and update chart using select options
- How to change properties of elements in a SVG when I mouseover an element of a second SVG using D3
- How to use d3.line().curve when using x1, y1, and x2, y2?
- How to show the unhighlighted/ not selected data points on scatter plots when using brush/group in dc.js? And is multiple brushes possible in dc.js
- How do I take into account scale when getting height/width of a d3 element using bounding rectangle
- How to fix viz that is getting out of sync when I change browser tabs?
- How do I change the colour of my d3.js Bar Graph bars individually and make a Scale for them?
- How to remove unwanted lines in Axis scale of a graph when using D3.js?
- how to adjust width and height when i click the rect in multilevel treeemap using d3.js?
- How to keep mouse over object when using drag and snap to with d3.js
- How to know if user is zooming and in which direction using d3 behavior zoom?
- How to do SVG translation when using time scale on X-axis in D3
- How to slow down the speed of zooming functionality using Javascript or D3 JS
- How do you query your dataset when you zoom into and out of a d3 map?
- How can I access and change variable in concatenated text of tooltip using D3/javascript?
- How to update and insert into databse using PHP when array in Javascript is updated?
More Query from same tag
- syncing d3.js with THREE.js earth
- Why selectAll("element") is important in d3js while generating a chart
- visx, how to grow pie chart arc on mouseEnter?
- JavaScript: "SyntaxError: missing ) after argument list" last line of file
- update graph with new data
- React App: D3 Chart not showing up
- D3 nested objects should be different colors
- D3 General Update Pattern transitions of circles/text inside of g element
- Transition partition arcs back to original angles
- D3 bar chart - last bar overhangs the end of the X axis
- Use words as tick labels with unequal intervals in D3.js
- D3 zoom is not properly reseting zoom state and translated location
- Target other elements on d3.js on event
- d3.js: Help understanding parameters to callback functions
- Pie layout using multiple values within a single object
- d3 - when setAttribute() works but .attr doesn't?
- curved text around D3.js pie chart
- change a solid link to a particular node into dashed one programmatically in d3
- D3.js not showing labels on first SVG after second SVG created
- Inject object d3.js drag and drop
- How do I use the d3 selection/join methods to change a line graph?
- How to update a single data point in d3 without touching other elements?
- Draw curve between draggable points with control points to adjust curve SVG and d3.js
- Blur/Shadow for Shapes in react native with d3.js
- D3.js - how can I iterate through subarrays in my dataset
- d3.js layout for dependencies graph?
- How to set x-axis values in d3 Javascript graph?
- $watch in directives and activate directive on click
- n3-line-chart: Inconsistent values for dates on x axis
- Bar chart using d3