由canvas中的文字背景色所引出的

3,940 阅读4分钟

前言

最近在处理一个需求,给canvas中的文字加上背景色,达到与dom相同的效果,google、baidu找了一遍,完全没找到相关的文章。

首先

拆分问题,如果只给【背景A】加上背景色,应该怎么做,这就很简单了,代码如下,上下边距再适当调整即可。

const text = '背景A';
ctx.fillStyle="red";
ctx.font = "24px Microsoft YaHei"
const { width } = ctx.measureText('背景A');
ctx.fillRect(100, 100, width, 24);
ctx.fillStyle="black";
ctx.textBaseline = 'top';
ctx.fillText("背景A",100,100);

但是

文字不只有【背景A】,还有一个字体不一样的【背景B】,两个文字对齐方式为【基线对齐】,并不是顶部对齐。

我们已知的只有文字大小,以及下图中的x,yBaseline。y是完全不知道的。由于y不知道,我们无法确定红色背景的位置。

为了解决这个问题

我们需要去了解字体的构造(在下精力有限。。。这里只介绍英文字体)

em

在css中,我们经常会去设置文字的font-size属性,当设置文字大小为16px时,实际绘制的文字(这里字体为Arial)如图所示,文字实际的高度并没有占满16px。

在字体中,存在一个em的概念,最初的英文排版中,将大写字母M的宽度作为1em,我们所设置的字体大小即1em的大小。em 是字体设计者在设计字体时的一个参考,并没有硬性规定,字体的高度一定要小于1em,或者字体的位置一定要在1em内。举个例子,microsoft YaHei字体,可以明显看到字体的下面已经超出了1em的最底部。

文字中的线

  • top:字体的最顶端
  • ascentder:上升,指图中小写字母高于mean line的部分。它的存在让小写字母更加易读
  • ascent:指小写字母最顶端
  • capHeight:指大写字母H的高度
  • mean line:位于小写字母x的最顶端
  • baseline:文字绘制的基准线,大写字母一般都在基准线之上绘制。
  • xHeight:即小写字母x的高度,这是字体设计中一个非常重要的概念,它定义了一个小写字母主体应该有多高。
  • descender:下降,指图中小写字母低于baseline的部分。与上升相对应
  • descent:小写字母最底端
  • bottom:字体的最底端

如何在web中测量出上面这些度量值

测算方法

测量方法是将特定的字母绘制到canvas中,通过getImageData读取图像数据,计算得出结果。 比如测算字母x的高度

  1. 将字母x绘制为黑色到canvas中
  2. 获取绘制区域的data developer.mozilla.org/zh-CN/docs/… ,即像素点数据
  3. 从前到后遍历data找到第一个非255的数据,这个点即x的左上角位置,下标除以绘制区域的宽度,向下取整,即是它到顶部的距离m。
  4. 从后往前遍历data找到最后一个非255的数据,这个点即x的右下角位置,下标除以绘制区域的宽度,向下取整即是它到顶部的距离n。
  5. x的高度即等于m-n。

通过这个方式,我们可以测得任意文字的高度。

Font Metrics

*以下所说的top、bottom、ascent、descent的大小为这些线到baseline的距离

  • xHeight
    • 通过测量字母x的高度可得出
  • Cap height
    • 通过测量字母H的高度可得出
  • ascent
  • descent
    • 绘制文字时指定绘制的基线为文字的基线,设置基线位置为yBase,将所有小写字母绘制到canvas中,可得到最高点为yTop,最低点yBottom,那么ascent = yTop - yBase,descent = yBase - yBottom
  • Ascender
    • 文字上升部分的高度,ascender = ascent - xheight
  • desender
    • 文字下降部分的高度,大小等于descent
  • top
    • 绘制文字时指定绘制的基线为文字的顶部,即ctx.textBaseline = top,设置基线位置为yBase,将字母A绘制到canvas中,可得到最低点yBottom,那么文字最顶部到基线的距离top = yBottom - yBase
  • bottom
    • 文字最顶部到最底部的距离为1em,那么文字最低部到基线的距离bottom = 1em - top

回到最开始的问题

那么y的值 y = yBaseline - FontMetrics.top。 绘制【背景B】的背景时同理可解。

结语

写文章可太难了QAQ,如果文中有任何描述错误或者需要讨论的地方,欢迎评论区指出。

参考资料