使用 CSS 制作圆形文本?

2025-05-25

使用 CSS 制作圆形文本?

你有没有想过把一些文字排成一个圆圈,但感觉像在黑暗中摸索一样费劲?今天你会怎么做呢?你能只用 CSS 就能准确地做到这一点吗?让我们来看看一种新方法 👀

今天如何解决这个问题

你肯定有很多选择。在我们讨论 CSS 之前,你可以使用图片。最难维护的方式可能是每次需要更改文本时都创建一个图片。确保使用alt属性来描述显示的文本。

Figma 中生成的圆形文本

Figma 中生成的 SVG 根据每个字母的位置为其单独创建一条路径。

或者你也可以使用内联 SVG。SVG 有一个textPath元素。在大多数情况下,它都很好用。你只需要给它一些文本和一个放置文本的路径。



<svg
  viewBox="0 0 100 100"
  xmlns="http://www.w3.org/2000/svg"
>
  <path
    id="circlePath"
    d="
      M 10, 50
      a 40,40 0 1,1 80,0
      40,40 0 1,1 -80,0
    "
  />
  <text>
    <textPath href="#circlePath">
      Your text here!
    </textPath>
  </text>
</svg>


Enter fullscreen mode Exit fullscreen mode

这条路线确实带来了一些障碍。首先,你需要一个path。你不能传递对 的引用circle。这意味着要将 转换circlepath



<path
  d="
    M (CENTER_X - RADIUS),CENTER_Y
    a RADIUS,RADIUS 0 1,1 (2 * RADIUS),0
    RADIUS,RADIUS 0 1,1 (-2 * RADIUS),0
  "
/>


Enter fullscreen mode Exit fullscreen mode

接下来,您需要选择是否使用textLength属性textPath。该属性可以使文本沿着路径展开。该属性的值就是周长。



<textPath
  href="#circularPath"
  textLength={Math.floor(Math.PI * 2 * RADIUS)}
>
  Your text here!
</textPath>


Enter fullscreen mode Exit fullscreen mode

把所有东西放在一起,你就会得到类似这样的东西。

在这个演示中,您可以更改文本并了解 的使用效果textLength。您还可以更改半径和字体大小。使用 SVG 的一大优势在于它具有可扩展性。另一个很酷的功能是文本会跟随路径方向。这样可以轻松翻转文本是在路径内部还是外部。您可以通过更改之间的圆弧扫描标志来实现这一点。10

所以 SVG 可以胜任这个工作,对吧?嗯,有点儿。但是,如果你尝试插入一些较长的文本,半径就需要调整了。而且,现在你就像在黑暗中摸索一样。


在大多数情况下,编写代码时,我们多少会假设一定程度的准确性。也就是说,除了舍入和其他 JavaScript 特有的技巧之外,其他方面都存在问题。所以对我来说,把数字输入到“看起来正确”之前,有时不太舒服。

你能用 CSS 更精确地表达吗?一个基本的方法是这样的:

  1. 将文本拆分为针对每个字符的 span 元素。
  2. 为字符索引设置内联自定义属性。
  3. 在父级上设置内联自定义属性以获取总字符数。
  4. 使用 CSS 计算,通过旋转来转换每个跨度,然后使用其索引进行平移。
  5. 如果屏幕阅读器可以看到,请使用 隐藏所有字符aria-hidden。然后创建一个视觉上隐藏的 span,其中包含全文。

HTML 可能看起来是这样的:



<span
  class="text-ring"
  style="--total: 15;"
>
  <span aria-hidden="true">
    <span style="--index: 0">Y</span>
    <span style="--index: 1">o</span>
    <span style="--index: 2">u</span>
    <!-- Obfuscated markup -->
  </span>
  <span class="sr-only">
    Your text here!
  </span>
</span>


Enter fullscreen mode Exit fullscreen mode

CSS 可能有点像这样:



.text-ring {
  position: relative;
}
.text-ring [style*=--index] {
  font-size: calc(var(--font-size, 2) * 1rem);
  position: absolute;
  top: 50%;
  left: 50%;
  transform:
    translate(-50%, -50%)
    rotate(calc(360deg / var(--total) * var(--index)))
    translateY(calc(var(--radius, 5) * -1ch));
}


Enter fullscreen mode Exit fullscreen mode

那么,半径该如何计算呢?我们仍在摸索中。请尝试在这个演示中调整,直到它们“看起来正确”为止。

输入三角函数

尽量不要害怕“三角函数”这个词。我知道,我知道。我一听到它,就会想到“微分方程”、“力学”等等。但是,一些数学知识和对字体行为的理解能帮我们找到答案。

数学女士模因

你可能注意到上面的演示使用了等宽字体。这样做的好处是,每个字符的宽度都相同。换个角度想想圆。它实际上是围绕一个点的很多三角形。等宽字体意味着圆的每一条“边”宽度都相同。如果知道每条边的宽度和内角,就可以计算出斜边。这个斜边就是半径!



const TOTAL_CHARACTERS = 15
const INNER_ANGLE = 360 / 15
const LENGTH_OF_SIDE = 1 // 1ch
const RADIUS = LENGTH_OF_SIDE /
  Math.sin(INNER_ANGLE / (180 / Math.PI))


Enter fullscreen mode Exit fullscreen mode

这里有个巧妙的技巧,就是用“ch”单位来定义边宽。这相当于我们字体中“0”的宽度。由于使用的是等宽字体,所以这个宽度就是每个字符的宽度。这意味着我们将得到一个以“ch”为单位的半径。但是,改变宽度font-size会导致文本环缩放。太棒了!

上面的 JavaScript 代码片段获取了半径,现在我们可以在 CSS 中设置它了。好处是,如果您正在生成静态标记,则可以将所有细节作为内联样式传递。这里是React 组件。您可以进行服务器端渲染,或者在 Astro 之类的组件(本网站使用的组件)中使用它。



const TextRing = (text) => {
  const CHARS = text.split('')
  const INNER_ANGLE = 360 / CHARS.length
  return (
    <span
      className="text-ring"
      style={{
        '--total': CHARS.length,
        '--radius': 1 / Math.sin(INNER_ANGLE / (180 / Math.PI))
      }}
    >
      {CHARS.map((char, index) => (
        <span style={{'--index': index }}>
          {char}
        </span>
      ))}
    </span>
  )
}


Enter fullscreen mode Exit fullscreen mode

在大多数情况下,这都能解决问题。但是,如果你不喜欢用 JavaScript 指定布局样式,该怎么办?CSS 也许很快就能帮上忙。

偶然的一件事。学校里的高等数学没达标,让我走上了编程之路。不过,那是以后的故事了。

CSS 三角函数简介

三角函数即将加入 CSS。这将使您可以在 CSS 中使用诸如sincos和 等函数tan。这些函数已在 Firefox 和 Safari 中可用。它们已在 Chromium 111 中发布。请务必查看“CSS 值和单位模块 4 级”规范。这些函数在 CSS 中的引入开辟了一些激动人心的机遇。例如,您可以在布局或加载动画的时间中使用它们

这对我们的圆形文本意味着什么?CSS 现在可以进行半径计算了。您不再需要将布局样式与 JavaScript 代码绑定。--radius现在,我们可以sin在 CSS 中使用 来计算半径。



.text-ring {
  --character-width: 1; /* In "ch" units */
  --inner-angle: calc((360 / var(--total)) * 1deg);
  --radius: calc(
    (
      var(--character-width, 1) /
      sin(var(--inner-angle))
    ) * -1ch
  );
}
.text-ring [style*=--index] {
  transform:
    translate(-50%, -50%)
    rotate(calc(var(--inner-angle) * var(--index)))
    translateY(var(--radius, -5ch));
}


Enter fullscreen mode Exit fullscreen mode

将其插入我们的演示中,我们就会得到正确的半径。

如果您使用的浏览器不支持三角函数,则应该会看到一条警告消息。您可以使用以下方法在 CSS 中检测是否支持三角函数:



@supports (top: calc(sin(1) * 1px)) {
  /* Insert styles */
}


Enter fullscreen mode Exit fullscreen mode

或者在 JavaScript 中使用:



const canTrig = CSS.supports('(top: calc(sin(1) * 1px))')


Enter fullscreen mode Exit fullscreen mode

作为最后的润色,让我们让文本环旋转:



@media (prefers-reduced-motion: no-preference) {
  .text-ring {
    animation: spin 6s infinite linear;
  }
  @keyframes spin {
    to {
      rotate: -360deg;
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

这样就完成了。把这些放在一起,我们就可以制作一个像这样的演示了。

在此演示中,如果 CSS 三角函数受支持,则会使用它们。如果不支持,我们将回退到使用--radiusJavaScript 设置。目前,您可以复制所需的 HTML 和 CSS,直到 CSS 三角函数获得完全支持。下一步是在 CSS 中获取sibling-countsibling-index。CSSWG 代码库中有一个相关提案。这样我们就不再需要内联自定义属性了。


就是这样!想想 Web 平台的进化真是令人难以置信。所有这些新功能和工具正在打破我们一直以来面临的障碍。

直到下一次,保持精彩!

ʕ •ᴥ•ʔ

进一步阅读

文章来源:https://dev.to/jh3y/circular-text-with-css-57jf
PREV
CSS Cyber​​punk 2077 按钮 - 带你的 CSS 去夜之城
NEXT
CSS 动画指南 - 第一部分 开始之前 那么,为什么要动画?我们可以为哪些内容制作动画? 我们的第一个动画 动画检查器 @keyframes 第一部分就到这里啦🤓