首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >d3正在同步2个独立的缩放行为

d3正在同步2个独立的缩放行为
EN

Stack Overflow用户
提问于 2020-04-07 09:07:48
回答 4查看 1K关注 0票数 25

我有下面的d3/d3fc图表

https://codepen.io/parliament718/pen/BaNQPXx

该图表具有针对主区域的缩放行为和针对y轴的单独缩放行为。可以拖动y轴以重新缩放。

我正在解决的问题是,在拖动y轴重新缩放,然后平移图表后,图表中出现了一个“跳跃”。

显然,这两个缩放行为有一个脱节,需要同步,但我正在绞尽脑汁试图解决这个问题。

代码语言:javascript
复制
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);
    });

所需的行为是缩放、平移和/或重新缩放轴,以便始终从上一个操作完成的位置应用变换,而不会发生任何跳转。

EN

回答 4

Stack Overflow用户

发布于 2020-04-10 21:00:47

好了,我又试了一次--正如我的previous answer中提到的,你需要克服的最大的问题是d3缩放只允许对称缩放。这是widely discussed一直在做的事情,我相信Mike Bostock将在下一个版本中解决这个问题。

所以,为了克服这个问题,你需要使用多重缩放行为。我已经创建了一个具有三个轴的图表,一个用于每个轴,一个用于绘图区。X和Y缩放行为用于缩放轴。每当X&Y缩放行为引发缩放事件时,其平移值将被复制到绘图区。同样,当绘图区域发生平移时,x&y分量被复制到各自的轴行为。

在绘图区域上缩放有点复杂,因为我们需要保持纵横比。为了实现这一点,我存储了之前的缩放转换,并使用缩放增量计算出适用于X&Y缩放行为的合适缩放比例。

为了方便起见,我将所有这些都封装到一个图表组件中:

代码语言:javascript
复制
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;
};

这是一支钢笔,上面有工作示例:

https://codepen.io/colineberhardt-the-bashful/pen/qBOEEGJ

票数 16
EN

Stack Overflow用户

发布于 2020-04-07 14:08:45

你的代码有几个问题,一个是容易解决的,另一个不是……

首先,d3-zoom的工作原理是在选定的DOM元素上存储一个转换-您可以通过__zoom属性看到这一点。当用户与DOM元素交互时,将更新此转换并发出事件。因此,如果您必须使用不同的缩放行为,这两种行为都控制单个元素的平移/缩放,则需要保持这些转换同步。

可以按如下方式复制变换:

代码语言:javascript
复制
selection.call(zoom.transform, d3.event.transform);

但是,这也会导致从目标行为中触发缩放事件。

另一种方法是直接复制到“stashed”转换属性:

代码语言:javascript
复制
selection.node().__zoom = d3.event.transform;

然而,你要实现的目标有一个更大的问题。将d3缩放变换存储为变换矩阵的3个分量:

https://github.com/d3/d3-zoom#zoomTransform

因此,缩放只能表示与平移一起的对称缩放。应用于x轴的非对称缩放不能由该变换忠实地表示,也不能重新应用于绘图区。

票数 7
EN

Stack Overflow用户

发布于 2020-04-12 05:54:32

这是an upcoming feature,正如@ColinE已经指出的那样。原始代码总是执行与变换矩阵不同步的“时间缩放”。

最好的解决方法是调整xExtent范围,以便图形相信侧面有额外的蜡烛。这可以通过在侧面添加衬垫来实现。accessors,非但不是,

代码语言:javascript
复制
[d => d.date]

变成了,

代码语言:javascript
复制
[
  () => 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

代码语言:javascript
复制
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.highd.low来调整yExtent,甚至可以同时使用xExtentyExtent来避免使用变换矩阵。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61071276

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档