How to temporarily disable scaling in d3.js

I'm looking for an option to temporarily disable the zoom feature provided by the d3 library. I tried to save the cave with the current zoom / translation values โ€‹โ€‹when the zoom is off, and set the zoom / translation values โ€‹โ€‹when the zoom is active again. Unfortunately this will not work.

Here is an example of the code I created:

var savedTranslation = null; var savedScale = null; var body = d3.select("body"); var svg = body.append("svg"); var svgContainer = svg.append("svg:g"); var circle = svgContainer.append("svg:circle") .attr('cx', 100) .attr('cy', 100) .attr('r',30) .attr('fill', 'red'); circle.on('click', clickFn); function clickFn(){ if (circle.attr('fill') === 'red'){ circle.attr('fill','blue') } else if (circle.attr('fill') === 'blue'){ circle.attr('fill','red') } }; svg.call(zoom = d3.behavior.zoom().on('zoom', redrawOnZoom)).on('dblclick.zoom', null); function redrawOnZoom(){ if (circle.attr('fill') === 'red'){ if (savedScale !== null){ zoom.scale(savedScale) savedScale = null } if (savedTranslation !== null){ zoom.translate(savedTranslation) savedTranslation = null } // the actual "zooming" svgContainer.attr('transform', 'translate(' + d3.event.translate + ')' + ' scale(' + d3.event.scale + ')'); } else { // save the current scales savedScale = zoom.scale() savedTranslation = zoom.translate() } }; 

Here is a working jsfiddle example.

EDIT:

False behavior can be reproduced in the following steps:

  • Click on the circle, the color will change to blue, scaling does not work.
  • Use the mouse wheel in ONE DIRECTION several times, as if you were zooming in (e.g. zoom)
  • Click on the circle again, colored red, zooming on again.
  • Use the mouse wheel, the circle will be huge / tiny.
+7
javascript svg behavior zoom
source share
7 answers

The easiest way I've found is to simply turn off all .zoom events when selected. You will need to re-call zoom to enable the behavior again.

 if (zoomEnabled) { svg.call(zoom); } else { svg.on('.zoom', null); } 

jsfiddle

+5
source share

I struggled with the same problem. And I found a solution that saves scalability and translation without the enhancement that you see with the current solution.

The main change is to save / update the scaling and transfer to the "click" function. And for access to the zoom function to be available, the click must be set after zooming. The solution looks like this. The same pattern from your problem:

 var savedTranslation = null; var savedScale = null; var body = d3.select("body"); var svg = body.append("svg"); var svgContainer = svg.append("svg:g"); var circle = svgContainer.append("svg:circle") .attr('cx', 100) .attr('cy', 100) .attr('r',30) .attr('fill', 'red'); 

Then the zoom function without managing the saved scale and translation:

 svg.call(zoom = d3.behavior.zoom().on('zoom', redrawOnZoom)).on('dblclick.zoom', null); function redrawOnZoom(){ if (circle.attr('fill') === 'red'){ // the actual "zooming" svgContainer.attr('transform', 'translate(' + zoom.translate() + ')' + ' scale(' + zoom.scale() + ')'); } }; 

Finally, attach the click behavior below, with saving and adjusting the scale and translation:

 circle.on('click', clickFn); function clickFn(){ if (circle.attr('fill') === 'red'){ circle.attr('fill','blue') if (savedScale === null){ savedScale = zoom.scale(); } if (savedTranslation === null){ savedTranslation = zoom.translate(); } } else if (circle.attr('fill') === 'blue'){ circle.attr('fill','red') if (savedScale !== null){ zoom.scale(savedScale); savedScale = null; } if (savedTranslation !== null){ zoom.translate(savedTranslation); savedTranslation = null; } } }; 

Here is the working version: http://jsfiddle.net/cb3Zm/1/ .

However, the click event still occurs when the drag and drop occurs, and this does not seem ideal, but I have not yet been able to fix it.

+4
source share

See the updated script: http://jsfiddle.net/prayerslayer/La8PR/1/

There I reassign an empty zoom operation in the click handler.

 function clickFn(){ if (circle.attr('fill') === 'red'){ circle.attr('fill','blue'); svg.call( fake ); } else if (circle.attr('fill') === 'blue'){ circle.attr('fill','red'); svg.call( zoom ); } }; 

I believe there is a better solution, since mine probably introduces memory leaks.

The advantage over the doZoom global flag is that you do not need to save and check the zoom and translation values โ€‹โ€‹because the zoom behavior continues to work (for example, setting d3.event.scale ) even if you do not change the view.

+2
source share

Yabba Dabba Doo!

Ok, the problem was

 else { // save the current scales savedScale = zoom.scale() savedTranslation = zoom.translate() } 

part. Values โ€‹โ€‹were called at each event not only once after the circle changed its color. So the solution was:

 else { // save the current scales if (savedScale === null){ savedScale = zoom.scale(); } if (savedTranslation === null){ savedTranslation = zoom.translate(); } 

and now IT WORKS! Updated jsFiddle here

+2
source share

I wanted to catch up with it when I found a solution! The trick is the reset scale, as well as the translation in the scaling and scaling events.

 var zoom = d3.behavior.zoom() .scaleExtent([1, 10]) .on("zoomstart", zoomstart) .on("zoomend", zoomend) .on("zoom", zoomed); function zoomed() { if (circle.attr('fill') === 'red') { if (savedScale !== null){ zoom.scale(savedScale); } if (savedTranslation !== null){ zoom.translate(savedTranslation); } svgContainer.attr('transform', 'translate(' + d3.event.translate + ')' + ' scale(' + d3.event.scale + ')'); } } function zoomend () { if (circle.attr('fill') === 'red') { if (savedScale !== null){ zoom.scale(savedScale); savedScale = null; } if (savedTranslation !== null){ zoom.translate(savedTranslation); savedTranslation = null; } } } function zoomstart (d) { if (circle.attr('fill') === 'red'){ if (savedScale !== null){ zoom.scale(savedScale) } if (savedTranslation !== null){ zoom.translate(savedTranslation) } } else { if (savedScale === null) { savedScale = zoom.scale(); } if (savedTranslation === null) { savedTranslation = zoom.translate(); } } } 
+2
source share

The way I implement this is a global flag that tells you whether scaling is enabled or not. Then you just need to check if this flag is set in the function that handles the scale. If so, the function does nothing.

+1
source share

I think itโ€™s more beautiful to do it.

 function clickFn(){ if (circle.attr('fill') === 'red'){ circle.attr('fill','blue'); savedScale = zoom.scale(); savedTranslation = zoom.translate(); } else if (circle.attr('fill') === 'blue'){ circle.attr('fill','red'); zoom.scale(savedScale); zoom.translate(savedTranslation); } }; svg.call(zoom = d3.behavior.zoom().on('zoom', redrawOnZoom)).on('dblclick.zoom', null); function redrawOnZoom(){ if (circle.attr('fill') === 'red'){ // the actual "zooming" svgContainer.attr('transform', 'translate(' + d3.event.translate + ')' + ' scale(' + d3.event.scale + ')'); } }; 
+1
source share

All Articles