在 D3 中放大和缩小时如何更改平移和缩放的速度(使用 zoom.on 和 d3.event.translate,d3.event.zoom)?
Posted
技术标签:
【中文标题】在 D3 中放大和缩小时如何更改平移和缩放的速度(使用 zoom.on 和 d3.event.translate,d3.event.zoom)?【英文标题】:How to change speed of translate and scale when zooming in and out in D3 (using zoom.on & d3.event.translate, d3.event.zoom)? 【发布时间】:2012-11-09 08:03:30 【问题描述】:当用户使用鼠标滚轮滚入和滚出时,您可以调整缩放速度吗?
我的理解是 zoom.on (https://github.com/mbostock/d3/wiki/Zoom-Behavior#wiki-on) 监听器产生两个事件 d3.event.translate 和 d3.event.zoom,它们包含矩阵或坐标,当传递给平移或缩放函数时,允许平移和重新缩放图形。
但是我如何加快速度,这样如果用户稍微移动鼠标滚轮,她就会快速放大或缩小?我有一个大型可视化,我想让用户使用鼠标滚轮快速放大和缩小。我可以简单地修改/添加上述现有事件和函数的参数,还是必须创建自己的?我觉得上面的一些内容在理解方面是不准确/不完整的,所以请解释一下。
这里非常简单的 jsfiddle 示例:http://jsfiddle.net/fiddler86/6jJe6/,下面有相同的代码:
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()
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
;
【问题讨论】:
你发现了吗? @kishanio 我提出了一个解决方案 【参考方案1】:当您选择函数时,您需要使用数学函数调整函数内部的比例,重要的是对于 x=0 y=0,您可以使用 pow 在这种情况下更容易Math.pow(d3.event.scale,.1)
第二个参数进行缩放越小越慢。
使用非常复杂的函数不是一个好主意,因为浏览器会变慢。
当你有了新的比例时,你需要重新计算翻译。你不会使问题复杂化,在 SVG 中你有this.getBBox().height
的实际高度,这没关系,但这并不完全是因为你落后了一个迭代。您可以使用(originalHeight * scale)
计算新高度,使用(originalHeight - (originalHeight * scale))/2
进行翻译
原来的高度*比例是新的高度
originalHeight - newHeight 是不同的,你想要 中心,你需要除以 2,正方形的一半和 下半部分。
现在我们需要对宽度进行操作。都一样
代码:
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+ ")");
;
注意我把200和300硬编码了,可以使用属性,使用常量...
我创建了一个提琴手:http://jsfiddle.net/t0j5b3e2/
【讨论】:
【参考方案2】:我已将 mbostock 的 drag+zoom example 修改为 4 倍变焦速度并将其放入 jsfiddle。我在下面解释了我的想法。这是我第一次尝试堆栈溢出答案,请善待。
正如 Raúl Martín 的回答中所述,您可以使用 redraw()
函数中的公式来更改缩放率。您需要执行一些额外的步骤,以确保 d3 行为在修改后的缩放率下仍能正常工作。
以选定点为中心进行缩放(例如光标)
默认情况下,d3 行为将缩放集中在鼠标指针上,例如如果您将鼠标指针放在图像的左上角,它会放大左上角而不是图像的中心。为了获得这种效果,它缩放图像,然后改变图像的平移,使鼠标光标下的点保持在屏幕上的相同位置。这就是为什么当您滚动鼠标滚轮时zoom.translate()
的值会发生变化,即使图像看起来不像在屏幕上移动。
如果您更改缩放速度,d3 zoom.translate()
值将不再正确。要计算出正确的翻译,您需要知道以下信息(忽略数值):
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
计算new_translate
以应用于图像的公式是:
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;
您可以将其与新比例一起应用到图像:
svg.attr("transform", "translate(" + new_translate + ")scale(" + new_scale + ")");
然后您必须更新 prev_scale = new_scale
和 prev_translate = new_translate
为 redraw()
的下一次迭代做好准备
无缩放平移
d3 缩放行为允许您通过单击和拖动进行平移而不进行缩放。如果单击并拖动,则 zoom.scale()
保持不变,但 zoom.translate()
会发生变化。即使您修改了缩放速度,新的zoom.translate()
值仍然正确。但是,您需要知道何时使用此 zoom.translate()
值以及何时使用计算的平移值来放大中心点。
您可以通过查看prev_scale
是否与new scale
相同来确定是平移还是缩放。如果这两个值相同,您就知道正在发生平移,您可以使用new_translate = zoom.translate()
移动图像。否则,您知道正在发生缩放,您可以如上所述计算new_translate
值。我通过向zoomstart
事件添加一个函数来做到这一点。
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 + ")");
【讨论】:
以上是关于在 D3 中放大和缩小时如何更改平移和缩放的速度(使用 zoom.on 和 d3.event.translate,d3.event.zoom)?的主要内容,如果未能解决你的问题,请参考以下文章