React 中的响应式 D3 图表:ResizeObserver 还是 viewBox 属性?

Responsive D3 Charts in React: ResizeObserver or the viewBox attribute?

我最近从有关 D3 + React 的教程系列中了解到 this video。在本视频中,为了调整图表的大小,创建了一个使用 ResizeObserver:

的自定义挂钩
const useResizeObserver = ref => {
  const [dimensions, setDimensions] = useState(null);
  useEffect(() => {
    const observeTarget = ref.current;
    const resizeObserver = new ResizeObserver(entries => {
      entries.forEach(entry => {
        setDimensions(entry.contentRect);
      });
    });
    resizeObserver.observe(observeTarget);
    return () => {
      resizeObserver.unobserve(observeTarget);
    };
  }, [ref]);
  return dimensions;
};

我以前总是看到/在 viewBox 属性之前使用它来使 SVG 具有响应性,正如 this article(以及许多其他人)所讨论的那样。我的问题是:是否有理由(性能、简单性)使用此 useResizeObserver 自定义挂钩而不是简单地使用 viewBox?使用 viewBox,我不需要创建自定义挂钩,或者在我的 SVG 周围使用包装器 div(正如他在视频中解释的那样),或者必须使用 refs.. .viewBox 看起来更简单。然而,显然 ResizeObserver 是一个新的 API 来获取元素的尺寸,也许使用它比 viewBox 有一些不明显的优势。

viewBox

的问题

虽然 viewBox 是最快的解决方案,但它有几个缺点:

  • viewBox 尝试 满足宽高比。 如果您的图表没有 1:1 的宽高比,则会发生这种情况: 图表不适合容器。相反 viewBox 保持纵横比并添加填充。我们可以用 preserveAspectRatio=none:
  • 来拉伸
  • 图表(和文本)缩放。不保留宽高比会引入另一个问题。如果您使用 heightwidth,您可以在 CSS 中将文本定义为特定大小 - 但不能使用 viewBox。以 0 0 600 300 viewBoxed 图表适合 1200*600 页 为例。在这种情况下,所有内容 都会缩放,包括文本,如上所示:您无法再设置字体大小。这不会只发生在文本上:其他不能完美拉伸的元素(如圆圈)也会有同样的问题。

乍一看,viewBox似乎简单多了。但是由于上面突出显示的脚枪,viewBox 在实践中使用起来变得非常复杂。

手动缩放

解决方案是使用 heightwidth 手动调整图表大小,避免上述所有问题。假设我们有一个基于高度和宽度呈现自身的图表组件。过程变为:

  • 在第一次渲染时设置 widthheight,以适应容器。容器需要自己设置宽度和高度。
  • 在容器上使用 ResizeObserver,它会在其大小更改时通知我们。
  • 调整图表大小时,获取新的高度和宽度,并使用它们来呈现图表。

当手动设置高度和宽度时,我们只调整需要缩放的部分。例如,上面的条形图:

  • 我们可以根据图表的高度和宽度设置条形的大小。
  • 如果我们有文本,我们可以将其定位为 相对于 高度和宽度,但使文本大小 固定,这将不可能 viewBox,因为它可以扩展一切:

结论

  • 如果您的图表包含不受拉伸影响的元素(例如矩形,或者如果您不关心以上问题
  • 如果您有无法拉伸的元素(例如文本或圆圈)并且需要文本保持您指定的大小,请使用手动缩放

如果您不关心宽高比问题,并且可以解决放大或缩小文本的问题,那么 viewBox 可能是正确的选择,只要您知道它陷阱。

然而,在实践中,手动缩放在绝大多数情况下最终成为更好的选择,因为很少有图表中只有可拉伸元素的情况.

来源

图片取自Responsive SVG charts — viewBox may not be the answer