需要生成默认头像,请教 SVG 文字如何居中?

准备开发一个应用,需要给新用户自动生成一个默认头像,像本论坛采用 Discourse 那样,采用首字符 + 随机背景。

假如像 Discourse 那样 ImageMagick 之类的图片生成库的话,得用特定的中文字体,想到 SVG 图片是 XML 代码,很容易生成,但是文字怎么居中呢?水平 + 垂直方向都需要居中

我注意到腾讯云的控制面板就是采用 SVG 方案,不知道它是怎么实现的?

  • 这头像是服务器端还是浏览器生成的?
  • 字体是怎么选?不同系统的默认中文字体不一样
  • x=“18.5” y=“25.5” 坐标是如何计算的?我看了【徐】占 33x46,(70 - 33) / 2 = 18.3 能解释 x,但 (70 - 46) / 2 = 12,怎么解释 y?
  • 难道每一个汉字在 Pingfang SC 中都是同样长宽比?33x46

截屏2022-11-02 10.49.51

<svg width="70" height="70" viewBox="0 0 38 38" fill="none"
     xmlns="http://www.w3.org/2000/svg">
    <g>
        <rect x="1" y="1" width="36" height="36" rx="18" fill="#3C89DD"></rect>
        <text fill="#ffffff"
              x="18.5"
              y="25.5"
              font-family="PingFang SC"
              font-size="18"
              font-weight="500"
              style="text-anchor: middle;"
        >徐</text>
    </g>
</svg>

好办法,不会svg,不过我把你的代码复制过来,把width="70" height="70"值改成700,放大以后文字也是居中显示的。

我看Discourse(本站)默认头像只显示一个字母,这样好像也不需要ImageMagick 之类的图片生成库,只要26张图片就可以了,但对于中文用户肯定是不行的。

1 个赞

文本的 x/y 坐标确实有点费解。

不过这里的 width="70" height="70" 不重要(如楼上所说,改成 700 也不影响居中),关键是 viewBox="0 0 38 38",它确定了一个 38x38 的正方形(没有单位,无极放大)。

rect 在正方形内居中画了一个直径 36 的圆。

text 看不太懂了,汉字高大于宽,y 坐标应该更小才是。

另外,其实 text 并不完全居中(19(38/2) 更接近居中),加一个小的同心圆就看得很清楚了:

1 个赞

文本的默认 (x,y) 基准点不是左上角,而是文本的 (left, baseline),腾讯头像的文字对齐是 text-anchor: middle; 作用的结果,把这句删了,再画了几条辅助线就一目了然了:

  • 圆心的坐标 (19,19)
  • 文本 y 坐标是对齐的是默认 baseline。

svg-text-baseline-1

代码
<svg width="70" height="70" viewBox="0 0 38 38" fill="none" style="border:1px solid #cd0000;" xmlns="http://www.w3.org/2000/svg">
    <g>
        <rect x="1"  y="1"  width="36" height="36" rx="18" fill="#3C89DD"></rect>
        <rect x="8"  y="8"  width="22" height="22" rx="11" style="fill:#cd0000;stroke:#cd0000;stroke-width:0.5;"></rect>
        <rect x="0"  y="0"  width="19" height="19"  style="fill:none;stroke:blue;stroke-width:0.5;"></rect>
        <rect x="0"  y="19" width="19" height="19"  style="fill:none;stroke:blue;stroke-width:0.5;"></rect>
        <rect x="19"  y="0" width="19" height="19"  style="fill:none;stroke:blue;stroke-width:0.5;"></rect>
        <rect x="19"  y="19" width="19" height="19" style="fill:none;stroke:blue;stroke-width:0.5;"></rect>
        <text fill="#ffffff"
              x="19"
              y="19"
              font-family="PingFang SC"
              font-size="18"
              font-weight="500"
        >徐</text>
    </g>
</svg>

比起用绝对坐标,百分比可能是更好的选择。而且如果想要垂直方向对齐,必需把 baseline 往中间推:

代码
<svg width="70" height="70" viewBox="0 0 38 38" fill="none" style="border:1px solid #cd0000;" xmlns="http://www.w3.org/2000/svg">
    <g>
        <rect x="1"  y="1"  width="36" height="36" rx="18" fill="#3C89DD"></rect>
        <rect x="8"  y="8"  width="22" height="22" rx="11" style="fill:#cd0000;stroke:#cd0000;stroke-width:0.5;"></rect>
        <rect x="0"  y="0"  width="19" height="19"  style="fill:none;stroke:blue;stroke-width:0.5;"></rect>
        <rect x="0"  y="19" width="19" height="19"  style="fill:none;stroke:blue;stroke-width:0.5;"></rect>
        <rect x="19"  y="0" width="19" height="19"  style="fill:none;stroke:blue;stroke-width:0.5;"></rect>
        <rect x="19"  y="19" width="19" height="19" style="fill:none;stroke:blue;stroke-width:0.5;"></rect>
        <text fill="#ffffff"
              x="50%"
              y="50%"
              font-family="PingFang SC"
              font-size="18"
              font-weight="500"
              dominant-baseline="middle" 
              style="text-anchor: middle;"
        >徐</text>
    </g>
</svg>

y 坐标看起来还是有点偏上,51% 可能效果会更好。


我也是趁着这个问题自己顺便学习了一些基本的 svg 知识,总结起来主要有:

  1. svg 标签的 widthheight 定义的是画布绝对大小。
  2. viewBox 定义的是逻辑上的大小,也就是说它可以随画布放大缩小。所以应该以 viewBox 的大小来摆放元素。
  3. text 对齐的关键因素是 baseline 和 text-anchor 属性。
3 个赞