缩放和平移时如何避免轴移出界限

问题描述 投票:0回答:1

我正在使用 React 和 D3 (v7) 来制作堆积条形图。我添加了在 X 和 Y 方向上缩放和平移图表的功能。

缩放和平移时,元素将移动到图表边界之外。我添加了一个

clipPath
,它与条本身一起使用,因此当缩放/平移时,条不会被绘制到轴之外。但是,轴仍然超出范围(请参见下图的示例)。

此处正确位置 -

Axes in correct position without zoom or pan

与缩放和平移后的移动方式相比

Axes extending past each other while bars are appropriately clipped

如何避免这种情况发生?我尝试过使用

rescaleY
,这会破坏缩放功能,并且
rescaleX
不适用于波段刻度。

这是我的缩写代码:

const BarChart = ({
  data,
  dimensions = {
    chartOffset: {
      left: 10,
    },
    margin: {
      top: 50,
      right: 50,
      bottom: 50,
      left: 50,
    },
    width: 800,
    height: 500,
  },
  subgroups,
  colors,
  displayNames,
}) => {
  const svgRef = React.useRef(null);
  const {
    width, height, margin, chartOffset,
  } = dimensions;
  const svgWidth = width + margin.left + margin.right;
  const svgHeight = height + margin.top + margin.bottom;
  const xScale = myChartUtils.getXScale(); //scaleBand
  const yScale = myChartUtils.getYScale(); //scaleLinear

  function zoom() {
    const extent = [[0, 0], [width, height]];
    const svgEl = d3.select(svgRef.current);

    svgEl.call(d3.zoom()
      .scaleExtent([1, 20])
      .translateExtent(extent)
      .extent(extent)
      .on("zoom", zoomed));

      function zoomed(e) {
        const xAxis = d3.axisBottom(xScale);
        const yAxis = d3.axisLeft(yScale).ticks(5).tickSize(5);

        xScale.range([0, width - margin.right].map(d => e.transform.applyX(d)));
        yScale.range([height, 0].map(d => e.transform.applyY(d)));

        svgEl
          .selectAll(".bars rect")
          .attr('x', d => xScale(d.data.user))
          .attr('width', xScale.bandwidth())
          .attr('y', d => yScale(d[1]))
          .attr('height', d => yScale(d[0]) - yScale(d[1]))
        svgEl.selectAll('.x-axis').call(xAxis)
        svgEl.selectAll('.y-axis').call(yAxis)
      }
  }
React.useEffect(() => {
    // generate stacked bars
    const stackedGroups = d3
      .stack()
      .keys(subgroups)
      .value((obj, key) => obj[key].length)(data);
    const svgEl = d3.select(svgRef.current);
    const svg = svgEl
      .append('g')
      .attr("id", "plot")
      .attr(
        'transform',
        `translate(${margin.left + chartOffset.left},${
          margin.top + chartOffset.top
        })`,
      );

    svg.append("defs").append("clipPath")
      .attr("id", "clip")
      .append("rect")
      .attr("width", width)
      .attr("height", height);

    const plotArea = svg.append("g")
      .attr("clip-path", "url(#clip)");

    myChartUtils.drawXAxis(xScale, svg, height, width, margin, 'title');
    myChartUtils.drawYAxis(yScale, svg, height, 'title');
    
    plotArea
      .append('g')
      .attr("class", "bars")
      .selectAll('g')
      .data(stackedGroups, (d) => d.user)
      .enter()
      .append('g')
      .attr('fill', (d) => colors[d.key])
      .selectAll('rect')
      .data((d) => d)
      .enter()
      .append('rect')
      .attr('x', (d) => xScale(d.data.user))
      .attr('y', (d) => yScale(d[1]))
      .attr('height', (d) => yScale(d[0]) - yScale(d[1]))
      .attr('width', xScale.bandwidth())
      .style('opacity', 0.5)
      .call(zoom);
    
    // Clear svg content before adding new elements
    return function cleanup() {
      svgEl.selectAll('rect').remove();
    };
  }, [data, margin, colors]);
javascript css d3.js charts
1个回答
0
投票
const deviation = 0.001;
let k = event.transform.k;
xScale.range([0, width].map(d => event.transform.applyX(d)));
if (Math.abs(k - 1) < deviation) {
  xScale.range([0, width]);
}

我写了这个问题的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.