如何暂时禁用 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();
【讨论】:
zoomstart 和 zoomend 事件已重命名为 start 和 end 之一最新版本【参考方案6】:我实现这一点的方式是使用一个全局标志来告诉您是否启用了缩放。然后您只需要检查是否在处理缩放的函数中设置了此标志。如果是,该函数什么也不做。
【讨论】:
但是我应该如何实现该功能“什么都不做”,但我可以在某个点重新启用缩放,并且它会从我禁用它的点继续?我的可视化中有跳跃/移动,因为事件仍然会在鼠标滚轮/指针上做出反应。 您可以在每次函数执行某些操作时保存当前的缩放/翻译状态,然后在重新启用缩放时通过重新分配从该状态恢复。 这就是我试图实现的目标......您能否提供更详细的信息如何使用 dr.js 实现这一目标? 每次执行缩放功能时都将zoom.translate
和zoom.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 中的缩放的主要内容,如果未能解决你的问题,请参考以下文章