我希望将绘图的缩放行为配置为具有三种交互方式:
现在,我可以让后两个在这个小提琴中工作:https://jsfiddle.net/s1t7mrpw/6/
缩放功能如下:
function zoomed() {
if (d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.type === 'mousemove') {
view.attr("transform", d3.event.transform);
centerline.call(xAxis.scale(d3.event.transform.rescaleX(x)));
d3.event.transform.y = 0;
g.attr("transform", d3.event.transform);
} else {
current_transform = d3.zoomTransform(view);
current_transform.x = current_transform.x - d3.event.sourceEvent.deltaY;
// what do I do here to pan the axis and update `view`'s transform?
// centerline.call(xAxis.scale(d3.event.transform.rescaleX(x))); ?
// view.attr('transform', current_transform); ?
g.attr('transform', current_transform);
}
}
它使用以下代码块中的重新缩放术语:
它使用d3-xyzoom在x和y方向上进行独立缩放。
但是当用户只是滚动(不按控制键)时,我不知道如何在else
括号中平移轴。
我以前为wheel.zoom
使用了一个单独的触发器,但是随后我还需要处理该其他函数中的控制键。
我基本上希望mousemove
的默认缩放行为以及按下控制键时的默认缩放行为,但是在不按下控制键时平移而不是缩放滚动。
答案 0 :(得分:0)
我在此答案上使用d3-zoom,因为它更常见且是d3捆绑包的一部分。 d3-xyzoom作为一个附加模块,以d3-zoom为基础添加了一些有用的功能,因此该解决方案应与带有较小修订的xyzoom一起使用-但我认为不必使用它 >
主要挑战在于使用滚轮来应用平移更改而不是比例更改。粗略地讲,当发生无控制的滚动事件时,您可以抓住所选内容的转换(应该是节点),并通过获取滚动并为其创建x偏移量来更新它以平移视图矩形。 但是,您不会更新zoomTransform来使缩放引起的缩放变化无效-因此,当您通过拖动进行平移时,矩形会更改大小,因为您尚未更新缩放所使用的zoomTransform行为。同样,如果不考虑缩放比例,则可以关闭转换。
应用于元素的缩放变换(作为“变换”属性)与缩放行为所跟踪的缩放变换之间的关联是SO上许多麻烦的原因-但由于不需要使用元素的变换,它允许实现更自由的实现方式属性以应用缩放行为(在画布中有用,对非正统的事物(例如颜色)进行缩放等)。
我将在这里提出一个基本的解决方案。
首先,可以通过修改d3.event.transform
对象本身(或使用d3.zoomTransform(selection.node)
(其中选择是最初调用缩放的选择))来更新由缩放行为跟踪的缩放变换。
第二,当d3.event.transform
对象注册为按比例滚动时,我们需要覆盖此比例,并将此比例转换为翻译-需要时。为此,我们需要跟踪当前(事件前)的翻译和缩放比例-因此我们可以手动对其进行修改:
// Keep track of zoom state:
var k = 1;
var tx = 0;
function zoomed() {
var t = d3.event.transform;
// If control is not pressed and a wheel was turned, set the scale to last known scale, and modify transform.x by some amount:
if(!d3.event.sourceEvent.ctrlKey && d3.event.sourceEvent.type == "wheel") {
t.k > k ? tx += 40/k : tx -= 40/k;
t.k = k;
t.x = tx;
}
// otherwise, proceed as normal, but track current k and tx:
else {
k = t.k;
tx = t.x;
}
// Apply the transform:
view.attr("transform", "translate("+[t.x,0]+")scale("+[t.k,1]+")");
axisG.call(xAxis.scale(t.rescaleX(xScale)));
}
以上,我修改了t.k和t.k的转换,以适应需要滚动/滚动的事件。通过将t.k与存储的k值进行比较,我可以确定方向:通常是缩小还是放大事件,然后可以将其转换为当前转换值的变化。通过将t.k设置回原始的k值,我可以停止发生比例缩放的更改。
对于其他缩放事件,与往常一样, 除外,我存储t.x和t.k值以备以后使用,以便车轮事件应该转换而不是缩放。
注意:我已经设置了y缩放比例,并在转换中将其转换为硬编码零,因此我们完全不必担心y分量。
var width = 500;
var height = 120;
// Keep track of zoom state:
var k = 1;
var tx = 0;
// Some text to show the transform parameters:
var p = d3.select("body")
.append("p");
// Nothing unordinary here:
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var xScale = d3.scaleLinear()
.domain([0,100])
.range([20,width-20])
var xAxis = d3.axisTop(xScale);
var axisG = svg.append("g")
.attr("transform","translate(0,50)")
.call(xAxis);
var view = svg.append("rect")
.attr("width", 20)
.attr("height",20)
.attr("x", width/2)
.attr("y", 60)
var zoom = d3.zoom()
.on("zoom",zoomed);
svg.call(zoom)
function zoomed() {
var t = d3.event.transform;
// If control is not pressed and a wheel was turned, set the scale to last known scale, and modify transform.x by some amount:
if(!d3.event.sourceEvent.ctrlKey && d3.event.sourceEvent.type == "wheel") {
t.k > k ? tx += 40/k : tx -= 40/k;
t.k = k;
t.x = tx;
}
// otherwise, proceed as normal, but track current k and tx:
else {
k = t.k;
tx = t.x;
}
// Apply the transform:
view.attr("transform", "translate("+[t.x,0]+")scale("+[t.k,1]+")");
axisG.call(xAxis.scale(t.rescaleX(xScale)));
// Show current zoom transform:
p.text("transform.k:" + t.k + ", transform.x: " + t.x);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script>