繁体   English   中英

如何在 D3 v5 y 轴中仅正确缩放和平移 y 轴

[英]How to proper zoom and pan only the y-axis in D3 v5 y-axis

我正在使用 d3 v6 开发可视化,它基本上使用一组条目来显示水平层。 可视化如下所示: 可视化示例

需要注意的是,随着层数的增加,一些层变得不可能看到适当的规模减少。 所以我想制作一个缩放(和平移)功能,但它只能在 Y 轴上工作。 我搜索了很多并找到了本教程( https://observablehq.com/@d3/xy-zoom ),从中我做了一些更改以适应我的可视化:

      const svgHeight: any = d3.select(svgContainer.current).attr('height');
      const yScaleGlobal = d3
        .scaleLinear()
        .domain([0, maxYValues])
        .range([0, svgHeight - MARGINS.TOP - MARGINS.BOTTOM]);

      const yAxis = d3.axisLeft(yScaleGlobal).tickFormat((d: any) => `${d} m`);

      const gY = layerGroup.append('g').attr('class', 'y-axis');

      // z holds a copy of the previous transform, so we can track its changes
      let z = d3.zoomIdentity;

      // set up the ancillary zooms and an accessor for their transforms
      const zoomY = d3.zoom().scaleExtent([0.2, 5]);
      const ty = () => d3.zoomTransform(gY.node());

      gY.call(zoomY).attr('pointer-events', 'none');

      drawViz();

      const zoom = d3.zoom().on('zoom', function (e) {
        const t = e.transform;
        const k = t.k / z.k;
        const point = center(e, this);

        if (k === 1) {
          // pure translation?
          gY.call(zoomY.translateBy, 0, (t.y - z.y) / ty().k);
        } else {
          // if not, we're zooming on a fixed point
          gY.call(zoomY.scaleBy, k, point);
        }

        z = t;

        drawViz();
      });

      svg.call(zoom).call(zoom.transform, d3.zoomIdentity.scale(0.8)).node();

      function drawViz() {
        const yr = ty().rescaleY(yScaleGlobal);
        yAxis.scale(yr);
        gY.call(yAxis, yr);
        if (data) updateLayers(data, yr);
      }

      function center(event, target) {
        if (event.sourceEvent) {
          const p = d3.pointers(event, target);
          return [d3.mean(p, (d) => d[0]), d3.mean(p, (d) => d[1])];
        }
        return [svgWidth / 2, svgHeight / 2];
      }

updateLayers函数是这样的:

     const updateLayers = (data: LAYER_COMPONENT_TYPE[], yScale) => {
        const layerFill = getLayerFill(data);

        const rects = layerGroup.selectAll('rect').data(data);

        rects.exit().remove();

        const newLayers = rects
          .enter()
          .append('rect')
          .attr('x', 10)
          .style('opacity', 0.5)
          .attr('width', 300)
          .style('stroke', '#101010')
          .style('stroke-width', '1px');

        newLayers
          .merge(rects)
          .style('fill', (d: LAYER_COMPONENT_TYPE) => {
            if (!layerFill[`${d.fgdc_texture}.${d.from}`].url) {
              return layerFill[`${d.fgdc_texture}.${d.from}`];
            }
            svg.call(layerFill[`${d.fgdc_texture}.${d.from}`]);
            return layerFill[`${d.fgdc_texture}.${d.from}`].url();
          })
          .attr('y', (d: LAYER_COMPONENT_TYPE, i) => {
            if (i === 0) return yScale(d.from);
            return yScale(data[i - 1].to);
          })
          .attr('height', (d: LAYER_COMPONENT_TYPE, i) => {
            return yScale(d.to - d.from);
          });
      };

缩放和平移工作正常,整个可视化也很好,但 yScale 有一个奇怪的行为。 矩形的高度在缩放或平移时超出比例(我已经意识到高度的异常等于 yScale 处“0 m”点的距离) here you can view the problem

无论如何,我可以弄清楚代码有什么问题或导致异常的原因。 所以我需要一些帮助来解决这个问题

编辑 1:我正在使用 react btw

我已经提出了一个解决方案(使用更好更清洁的方法)。 该解决方案基于 TBD 的d3 时间线实现 使用他的解决方案,我只是适应了 y 轴和我的代码

    const maxYValues = d3.max(maxValues) || 0;

    const yScaleGlobal = d3
      .scaleLinear()
      .domain([0, maxYValues])
      .range([0, svgHeight - MARGINS.TOP - MARGINS.BOTTOM]);

    const yAxis = d3.axisLeft(yScaleGlobal).tickFormat((d: any) => `${d} m`);

    const gY = litoligicalGroup.select(`.${styles.yAxis}`).call(yAxis);

    const spanY = (d) => {
      if (d.thickness) return yScaleGlobal(0) - 10;
      return yScaleGlobal(d.from);
    };

    const spanH = (d) => {
      if (d.thickness) return 10;
      return yScaleGlobal(d.to - d.from);
    };

    const zoom = d3
      .zoom()
      .scaleExtent([0.2, 5])
      .on('zoom', (e) => {
        const transform = e.transform;

        gY.call(yAxis.scale(transform.rescaleY(yScaleGlobal)));
        // the viz group is where all the layers are attached
        vizGroup
          .selectAll('rect')
          .attr('y', (d) => {
            if (!d) return null;
            return transform.applyY(spanY(d));
          })
          .attr('height', (d) => {
            if (!d) return null;
            return transform.k * spanH(d);
          });
      });

    drawProfile();

    svg.call(zoom);

    function drawProfile() {
      if (geologicData) updateGeology(geologicData, yScaleGlobal);
      if (constructionData) updatePoco(constructionData, yScaleGlobal);
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM