How to temporarily disable the zooming in d3.js

对着背影说爱祢 提交于 2019-12-04 04:04:05

The easiest way I've found is to simply disable all the .zoom events on the selection. You'll have to re-call zoom to enable the behavior again.

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

jsfiddle

I have been struggling with the same problem. And, I have found a solution that saves zoom and translation without the jumpiness that you see with the current solution.

The main change is to perform the saving/updating of zoom and translate in the "click" function. And so that a reference to the zoom function is available, the click must be set after the zoom behavior. The solution looks like this. The same boilerplate 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 translate:

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 the saving and setting of 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 a working version: http://jsfiddle.net/cb3Zm/1/.

However, the click event still happens when a drag occurs, and that doesn't seem ideal, but I haven't been able to fix it yet.

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

There I reassign an empty zoom behavior 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 suppose there is a better solution as mine probably introduces memory leaks.

The advantage over a global doZoom flag is that you don't have to save and check scale and translation values because the zoom behavior continues to work (e.g. setting d3.event.scale) even though you're not altering the view.

Yabba Dabba Doo!

Ok, the problem was in the

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

part. The values were called on every 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.

I wanted to catch up on this as I found a solution! Trick is to reset scale and translate in the zoomstart- and zoomend-events as well.

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();
        }
     }
}

The way I would implement this is with a global flag that tells you whether zooming is enabled or not. Then you simply need to check whether this flag is set in the function that handles the zoom. If it is, the function does nothing.

I think it's more beautiful to do it this way.

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

};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!