自动缩放 SVG 以适应子内容的宽度(例如文本)[无 JS]

ras*_*soh 6 css svg

我发现的一切都是相反的,例如“如何缩放文本以适合 SVG”。但我想达到相反的效果:

我的 html 中的 SVG:

<svg class="text" xmlns='http://www.w3.org/2000/svg' stroke='black' fill='none'>
  <text>
    <tspan>Some Text</tspan>
  </text>
</svg>
Run Code Online (Sandbox Code Playgroud)

CSS:

svg.text {
  font-size: 2.5rem; // changes on different screen sizes
  height: 0.8em; 
  width: auto; // This doesn't work... the width becomes something weird, always the same, no matter the contents of the text 
}
svg.text text {
  transform: translateY(0.8em); // needed this to make the text visible at all
}
Run Code Online (Sandbox Code Playgroud)

我希望<svg>增长以匹配元素的宽度text,就像普通 DOM 元素设置为display: inline-block;or position: absolute;or一样float: left;

有任何想法吗?

编辑

感谢@enxaneta 的评论,我想出了这个:

JavaScript:

/**
 * Fit SVGs to their first text child's viewBox
 */
fitSvgTextElements() {
  const elements = document.querySelectorAll('svg.text');
  for( const el of elements ) {
    const box = el.querySelector('text').getBBox();
    el.setAttribute('viewBox', `${box.x} ${box.y} ${box.width} ${box.height}`);
  }
}
Run Code Online (Sandbox Code Playgroud)

这有效。但是有没有办法用纯 CSS/SVG 属性来解决这个问题呢?

ccp*_*rog 6

由于 SVG 格式的基本设计决策,如果不编写脚本就无法实现您想要实现的尺寸更改。该规范对相关概念的描述如下:

所有 SVG 内容都在SVG 视口内绘制。每个 SVG 视口都定义一个绘图区域,其特征在于尺寸(宽度、高度)和原点...

SVG 视口的宽度、高度和原点是通过生成 SVG 视口的 SVG 文档片段与该片段的父片段(无论是真实的还是隐式的)之间的协商过程确定的...

这意味着视口的尺寸取决于其外部上下文,但与其内部内容无关。

您尝试按其内容调整视口元素的大小会颠倒该逻辑顺序,并且只能在渲染最初完成后才能实现。此时,只有脚本可以更改渲染的 DOM。

如何进行脚本化解决方案,您已经在问题中演示了,尽管我会写得有点不同。虽然 CSS 规则width: auto; height: 1em 应该足够了,但一些较旧的浏览器在这方面存在问题。我更喜欢明确地计算元素width的样式<svg>。(width属性的特异性低于 CSS 样式表规则。)

fitSvgTextElements() {
  const elements = document.querySelectorAll('svg.text');
  for( const el of elements ) {
    const box = el.querySelector('text').getBBox();
    el.setAttribute('viewBox', `${box.x} ${box.y} ${box.width} ${box.height}`);
    // the <svg> element is in HTML context,
    // so you need dimensions in relation to the page viewport
    const viewport = el.getBoundingClientRect();
    // retain height, recompute width in relation
    const width = viewport.height * box.width / box.height;
    el.style.width = `${width}px`;
  }
}
Run Code Online (Sandbox Code Playgroud)


ras*_*soh 3

事实上,我发现了一种更干净的方法。魔法属性是显性基线,这里有提到。

HTML 中的 SVG(在元素dominant-baseline="hanging"上text):

<svg class="text" xmlns='http://www.w3.org/2000/svg'>
  <text dominant-baseline="hanging">
    My Text
  </text>
</svg>
Run Code Online (Sandbox Code Playgroud)

JS:

<svg class="text" xmlns='http://www.w3.org/2000/svg'>
  <text dominant-baseline="hanging">
    My Text
  </text>
</svg>
Run Code Online (Sandbox Code Playgroud)

...现在我可以在 CSS 中调整文本大小,例如:

/**
 * Fit SVGs to their first text child's width and height
 */
fitSvgTextElements() {
  const elements = document.querySelectorAll('svg.text');
  for( const el of elements ) {
    const box = el.querySelector('text').getBBox();
    el.style.width = `${box.width}px`;
    el.style.height = `${box.height}px`;
  }
}
window.addEventListener('load', fitSvgTextElements);
window.addEventListener('resize', fitSvgTextElements);
Run Code Online (Sandbox Code Playgroud)