如何暂时禁用 d3.js 中的缩放

Posted

技术标签:

【中文标题】如何暂时禁用 d3.js 中的缩放【英文标题】:How to temporarily disable the zooming in d3.js 【发布时间】:2013-09-18 06:02:47 【问题描述】:

我正在寻找暂时禁用 d3 库提供的缩放功能的可能性。我尝试在禁用缩放时将当前缩放/平移值保存在洞穴中,并在再次激活缩放时设置缩放/平移值。不幸的是,这行不通。

这是我创建的代码示例:

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

这是working jsfiddle example.

编辑:

可以通过以下步骤重现错误行为:

    点击圆圈,颜色变为蓝色,缩放不起作用 在一个方向上多次使用鼠标滚轮,就好像要进行缩放(例如放大) 再次点击圆圈,颜色变为红色,重新启用缩放 使用鼠标滚轮,圆圈会变大/变小

【问题讨论】:

【参考方案1】:

我发现的最简单的方法是简单地禁用选择中的所有 .zoom 事件。您必须重新调用 zoom 才能再次启用该行为。

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

jsfiddle

【讨论】:

【参考方案2】:

我一直在努力解决同样的问题。而且,我找到了一种解决方案,可以节省缩放和平移,而不会像当前解决方案那样出现跳动。

主要变化是在“点击”功能中执行缩放和翻译的保存/更新。为了使缩放功能的引用可用,必须在缩放行为之后设置单击。解决方案如下所示。与您的问题相同的样板:

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

然后是缩放功能,无需管理保存的比例和平移:

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

最后附上下面的点击行为,以及缩放和平移的保存和设置:

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;
         
    
; 

这是一个工作版本:http://jsfiddle.net/cb3Zm/1/。

但是,当发生拖动时,点击事件仍然会发生,这似乎并不理想,但我还没有能够修复它。

【讨论】:

【参考方案3】:

查看更新的小提琴:http://jsfiddle.net/prayerslayer/La8PR/1/

我在点击处理程序中重新分配了一个空的缩放行为。

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

我想有一个更好的解决方案,因为我的可能会引入内存泄漏。

相对于全局 doZoom 标志的优势在于,您不必保存和检查比例和平移值,因为即使您没有更改视图,缩放行为也会继续工作(例如设置 d3.event.scale) .

【讨论】:

感谢您的建议。问题是在我的项目中,我没有从 on('click', ... ) 方法分配缩放行为。 redrawOnZomm() 函数的行为应该根据圆的状态(颜色)。【参考方案4】:

雅巴达巴斗!

好的,问题出在

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

部分。每次事件都会调用这些值,而不仅仅是在圆圈改变颜色后调用一次。所以解决方案是:

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

现在它可以工作了! Updated jsFiddle here.

【讨论】:

这仍然只适用于缩放,如果你在禁用时拖动圆圈,重新启用它,然后再次拖动它,翻译仍然会有跳跃行为。【参考方案5】:

我想赶上这个,因为我找到了一个解决方案! 技巧是在 zoomstart- 和 zoomend- 事件中重置比例和平移。

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

【讨论】:

zoomstartzoomend 事件已重命名为 startend 之一最新版本【参考方案6】:

我实现这一点的方式是使用一个全局标志来告诉您是否启用了缩放。然后您只需要检查是否在处理缩放的函数中设置了此标志。如果是,该函数什么也不做。

【讨论】:

但是我应该如何实现该功能“什么都不做”,但我可以在某个点重新启用缩放,并且它会从我禁用它的点继续?我的可视化中有跳跃/移动,因为事件仍然会在鼠标滚轮/指针上做出反应。 您可以在每次函数执行某些操作时保存当前的缩放/翻译状态,然后在重新启用缩放时通过重新分配从该状态恢复。 这就是我试图实现的目标......您能否提供更详细的信息如何使用 dr.js 实现这一目标? 每次执行缩放功能时都将zoom.translatezoom.scale 的值保存在全局变量中,但如果禁用缩放功能则不会。然后,当重新启用缩放时,您将这些值分配回您的缩放行为。 这不是我在我的示例中所做的吗?问题是,缩放始终与鼠标滚轮/指针交互,因此 d3.event.scale/translate 值也会发生变化。我的问题是重新启用缩放后如何将这些值设置/恢复为保存的值。显然 zoom.scale(savedScale)/zoom.translate(savedTranslation) 不是诀窍。【参考方案7】:

我觉得这样做更漂亮。

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

;

【讨论】:

【参考方案8】:

https://github.com/d3/d3-zoom/issues/156

const svg = d3.select(svgElement);
const zoom = d3.zoom();

function startZoomPan() 
  svg.call(zoom); // attach the zoom listeners


function stopZoomPan() 
  svg.on('.zoom', null); // remove the zoom listeners

mousemove 处理程序是热代码,因此分支很慢

【讨论】:

以上是关于如何暂时禁用 d3.js 中的缩放的主要内容,如果未能解决你的问题,请参考以下文章

d3js中的缩放线

Javascript / D3.js - 绘制大型数据集 - 提高 d3.js 绘制的 svg 图表中的缩放和平移速度

在 D3.js 中缩放或平移时限制域

D3.JS“缩放”未定义

QWebEngineView 如何禁用捏合缩放

D3.js 设置初始缩放级别