使用 CSS 制作圆形文本?
你有没有想过把一些文字排成一个圆圈,但感觉像在黑暗中摸索一样费劲?今天你会怎么做呢?你能只用 CSS 就能准确地做到这一点吗?让我们来看看一种新方法 👀
今天如何解决这个问题
你肯定有很多选择。在我们讨论 CSS 之前,你可以使用图片。最难维护的方式可能是每次需要更改文本时都创建一个图片。确保使用alt
属性来描述显示的文本。
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>
这条路线确实带来了一些障碍。首先,你需要一个path
。你不能传递对 的引用circle
。这意味着要将 转换circle
为path
。
<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
"
/>
接下来,您需要选择是否使用textLength
属性textPath
。该属性可以使文本沿着路径展开。该属性的值就是周长。
<textPath
href="#circularPath"
textLength={Math.floor(Math.PI * 2 * RADIUS)}
>
Your text here!
</textPath>
把所有东西放在一起,你就会得到类似这样的东西。
在这个演示中,您可以更改文本并了解 的使用效果textLength
。您还可以更改半径和字体大小。使用 SVG 的一大优势在于它具有可扩展性。另一个很酷的功能是文本会跟随路径方向。这样可以轻松翻转文本是在路径内部还是外部。您可以通过更改和之间的圆弧扫描标志来实现这一点。1
0
所以 SVG 可以胜任这个工作,对吧?嗯,有点儿。但是,如果你尝试插入一些较长的文本,半径就需要调整了。而且,现在你就像在黑暗中摸索一样。
在大多数情况下,编写代码时,我们多少会假设一定程度的准确性。也就是说,除了舍入和其他 JavaScript 特有的技巧之外,其他方面都存在问题。所以对我来说,把数字输入到“看起来正确”之前,有时不太舒服。
你能用 CSS 更精确地表达吗?一个基本的方法是这样的:
- 将文本拆分为针对每个字符的 span 元素。
- 为字符索引设置内联自定义属性。
- 在父级上设置内联自定义属性以获取总字符数。
- 使用 CSS 计算,通过旋转来转换每个跨度,然后使用其索引进行平移。
- 如果屏幕阅读器可以看到,请使用 隐藏所有字符
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>
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));
}
那么,半径该如何计算呢?我们仍在摸索中。请尝试在这个演示中调整,直到它们“看起来正确”为止。
输入三角函数
尽量不要害怕“三角函数”这个词。我知道,我知道。我一听到它,就会想到“微分方程”、“力学”等等。但是,一些数学知识和对字体行为的理解能帮我们找到答案。
你可能注意到上面的演示使用了等宽字体。这样做的好处是,每个字符的宽度都相同。换个角度想想圆。它实际上是围绕一个点的很多三角形。等宽字体意味着圆的每一条“边”宽度都相同。如果知道每条边的宽度和内角,就可以计算出斜边。这个斜边就是半径!
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))
这里有个巧妙的技巧,就是用“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>
)
}
在大多数情况下,这都能解决问题。但是,如果你不喜欢用 JavaScript 指定布局样式,该怎么办?CSS 也许很快就能帮上忙。
偶然的一件事。学校里的高等数学没达标,让我走上了编程之路。不过,那是以后的故事了。
CSS 三角函数简介
三角函数即将加入 CSS。这将使您可以在 CSS 中使用诸如sin
、cos
和 等函数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));
}
将其插入我们的演示中,我们就会得到正确的半径。
如果您使用的浏览器不支持三角函数,则应该会看到一条警告消息。您可以使用以下方法在 CSS 中检测是否支持三角函数:
@supports (top: calc(sin(1) * 1px)) {
/* Insert styles */
}
或者在 JavaScript 中使用:
const canTrig = CSS.supports('(top: calc(sin(1) * 1px))')
作为最后的润色,让我们让文本环旋转:
@media (prefers-reduced-motion: no-preference) {
.text-ring {
animation: spin 6s infinite linear;
}
@keyframes spin {
to {
rotate: -360deg;
}
}
}
这样就完成了。把这些放在一起,我们就可以制作一个像这样的演示了。
在此演示中,如果 CSS 三角函数受支持,则会使用它们。如果不支持,我们将回退到使用--radius
JavaScript 设置。目前,您可以复制所需的 HTML 和 CSS,直到 CSS 三角函数获得完全支持。下一步是在 CSS 中获取sibling-count
和sibling-index
。CSSWG 代码库中有一个相关提案。这样我们就不再需要内联自定义属性了。
就是这样!想想 Web 平台的进化真是令人难以置信。所有这些新功能和工具正在打破我们一直以来面临的障碍。
直到下一次,保持精彩!
ʕ •ᴥ•ʔ
进一步阅读
- 三角函数 CSS 函数简介:Stephanie Stimac
- 自定义 CSS 属性的作用域的强大之处(和乐趣):CSS 技巧
- 文本路径:MDN
- 文本长度:MDN
- SVG 圆分解为路径:Smashing Magazine
- 演示集锦:CodePen