我有下面的d3/d3fc图表
https://codepen.io/parliament718/pen/BaNQPXx
该图表具有针对主区域的缩放行为和针对y轴的单独缩放行为。可以拖动y轴以重新缩放。
我正在解决的问题是,在拖动y轴重新缩放,然后平移图表后,图表中出现了一个“跳跃”。
显然,这两个缩放行为有一个脱节,需要同步,但我正在绞尽脑汁试图解决这个问题。
const mainZoom = zoom()
.on('zoom', () => {
xScale.domain(t.rescaleX(x2).domain());
yScale.domain(t.rescaleY(y2).domain());
});
const yAxisZoom = zoom()
.on('zoom', () => {
const t = event.transform;
yScale.domain(t.rescaleY(y2).domain());
render();
});
const yAxisDrag = drag()
.on('drag', (args) => {
const factor = Math.pow(2, -event.dy * 0.01);
plotArea.call(yAxisZoom.scaleBy, factor);
});所需的行为是缩放、平移和/或重新缩放轴,以便始终从上一个操作完成的位置应用变换,而不会发生任何跳转。
发布于 2020-04-10 21:00:47
好了,我又试了一次--正如我的previous answer中提到的,你需要克服的最大的问题是d3缩放只允许对称缩放。这是widely discussed一直在做的事情,我相信Mike Bostock将在下一个版本中解决这个问题。
所以,为了克服这个问题,你需要使用多重缩放行为。我已经创建了一个具有三个轴的图表,一个用于每个轴,一个用于绘图区。X和Y缩放行为用于缩放轴。每当X&Y缩放行为引发缩放事件时,其平移值将被复制到绘图区。同样,当绘图区域发生平移时,x&y分量被复制到各自的轴行为。
在绘图区域上缩放有点复杂,因为我们需要保持纵横比。为了实现这一点,我存储了之前的缩放转换,并使用缩放增量计算出适用于X&Y缩放行为的合适缩放比例。
为了方便起见,我将所有这些都封装到一个图表组件中:
const interactiveChart = (xScale, yScale) => {
const zoom = d3.zoom();
const xZoom = d3.zoom();
const yZoom = d3.zoom();
const chart = fc.chartCartesian(xScale, yScale).decorate(sel => {
const plotAreaNode = sel.select(".plot-area").node();
const xAxisNode = sel.select(".x-axis").node();
const yAxisNode = sel.select(".y-axis").node();
const applyTransform = () => {
// apply the zoom transform from the x-scale
xScale.domain(
d3
.zoomTransform(xAxisNode)
.rescaleX(xScaleOriginal)
.domain()
);
// apply the zoom transform from the y-scale
yScale.domain(
d3
.zoomTransform(yAxisNode)
.rescaleY(yScaleOriginal)
.domain()
);
sel.node().requestRedraw();
};
zoom.on("zoom", () => {
// compute how much the user has zoomed since the last event
const factor = (plotAreaNode.__zoom.k - plotAreaNode.__zoomOld.k) / plotAreaNode.__zoomOld.k;
plotAreaNode.__zoomOld = plotAreaNode.__zoom;
// apply scale to the x & y axis, maintaining their aspect ratio
xAxisNode.__zoom.k = xAxisNode.__zoom.k * (1 + factor);
yAxisNode.__zoom.k = yAxisNode.__zoom.k * (1 + factor);
// apply transform
xAxisNode.__zoom.x = d3.zoomTransform(plotAreaNode).x;
yAxisNode.__zoom.y = d3.zoomTransform(plotAreaNode).y;
applyTransform();
});
xZoom.on("zoom", () => {
plotAreaNode.__zoom.x = d3.zoomTransform(xAxisNode).x;
applyTransform();
});
yZoom.on("zoom", () => {
plotAreaNode.__zoom.y = d3.zoomTransform(yAxisNode).y;
applyTransform();
});
sel
.enter()
.select(".plot-area")
.on("measure.range", () => {
xScaleOriginal.range([0, d3.event.detail.width]);
yScaleOriginal.range([d3.event.detail.height, 0]);
})
.call(zoom);
plotAreaNode.__zoomOld = plotAreaNode.__zoom;
// cannot use enter selection as this pulls data through
sel.selectAll(".y-axis").call(yZoom);
sel.selectAll(".x-axis").call(xZoom);
decorate(sel);
});
let xScaleOriginal = xScale.copy(),
yScaleOriginal = yScale.copy();
let decorate = () => {};
const instance = selection => chart(selection);
// property setters not show
return instance;
};这是一支钢笔,上面有工作示例:
发布于 2020-04-07 14:08:45
你的代码有几个问题,一个是容易解决的,另一个不是……
首先,d3-zoom的工作原理是在选定的DOM元素上存储一个转换-您可以通过__zoom属性看到这一点。当用户与DOM元素交互时,将更新此转换并发出事件。因此,如果您必须使用不同的缩放行为,这两种行为都控制单个元素的平移/缩放,则需要保持这些转换同步。
可以按如下方式复制变换:
selection.call(zoom.transform, d3.event.transform);但是,这也会导致从目标行为中触发缩放事件。
另一种方法是直接复制到“stashed”转换属性:
selection.node().__zoom = d3.event.transform;然而,你要实现的目标有一个更大的问题。将d3缩放变换存储为变换矩阵的3个分量:
https://github.com/d3/d3-zoom#zoomTransform
因此,缩放只能表示与平移一起的对称缩放。应用于x轴的非对称缩放不能由该变换忠实地表示,也不能重新应用于绘图区。
发布于 2020-04-12 05:54:32
这是an upcoming feature,正如@ColinE已经指出的那样。原始代码总是执行与变换矩阵不同步的“时间缩放”。
最好的解决方法是调整xExtent范围,以便图形相信侧面有额外的蜡烛。这可以通过在侧面添加衬垫来实现。accessors,非但不是,
[d => d.date]变成了,
[
() => new Date(taken[0].date.addDays(-xZoom)), // Left pad
d => d.date,
() => new Date(taken[taken.length - 1].date.addDays(xZoom)) // Right pad
]Sidenote:注意,有一个的pad函数,但由于某些原因,它只工作一次,并且永远不会再更新,这就是为什么它被添加为accessors__。
旁注2:添加Function addDays作为原型(不是最好的做法)只是为了简单。
现在zoom事件修改了X缩放因子xZoom,
zoomFactor = Math.sign(d3.event.sourceEvent.wheelDelta) * -5;
if (zoomFactor) xZoom += zoomFactor;直接从wheelDelta. 读取差异非常重要这就是不受支持的特性所在:我们不能从t.x读取,因为即使你拖动Y轴,它也会改变。
最后,重新计算chart.xDomain(xExtent(data.series));,以便新的盘区可用。
在这里查看没有跳转的工作演示:https://codepen.io/adelriosantiago/pen/QWjwRXa?editors=0011
修复:缩放反转,改进了触摸板上的行为。
从技术上讲,你也可以通过添加额外的d.high和d.low来调整yExtent,甚至可以同时使用xExtent和yExtent来避免使用变换矩阵。
https://stackoverflow.com/questions/61071276
复制相似问题