小程序真机下会出现canvas
metrics
无法获取字体高度的问题,模拟器下表现正常。
背景描述
在一个项目下需要在canvas
中对一行文本垂直居中展示,以下是一个示例效果:
在模拟器上的表现很正常,但是到了真机上后文本位置出现严重的偏差。验证了iOS设备和Android设备都无法按预期做到垂直居中展示。
初步分析
通过输出metrics
返回的值,可以看到模拟器上是可以获取到width
和height
的,而在真机上只能获取到有效的width
,而height
的值为0。
所以通过下述代码进行文本绘制时就出现了异常:
1 | const metrics = context.measureText('中'); |
在真机上验证,metrics.fontBoundingBoxAscent
的返回值是undefined
,那么height / 2 + fontHeight / 2
的计算结果就是NaN
,因此在绘制的时候,就相当于在y
轴上绘制在坐标为0
的位置。
在绘制的时候,为了便于计算居中方便,设置了context.textBaseline = "bottom";
,因此如果在y
轴上坐标为0
的文字就完全不可见了。
关于textBaseline
的详细说明可以详见:MDN 和 w3schools
metrics
兼容性
印象中,metrics
在手机端浏览器的兼容性还是蛮好的,包括 Safari on iOS 和 WebView Android,如下图所示:
注:这里只截取了使用到的两个属性metrics.width
和metrics.fontBoundingBoxAscent
。
从截图上可以看出,metrics.width
在很早很早就支持了,而metrics.fontBoundingBoxAscent
的支持在各个平台上支持的时间都比较晚。
- chrome 87:发布时间2020年,详见Chrome Archived release notes
- safari 11.3:发布时间2018年,详见Safari version history
- Firefox:默认不支持,需要开启开关后才支持
- IE:完全不支持
我目前使用的iOS手机上的safari版本是15.4,是完全支持metrics.fontBoundingBoxAscent
这个属性的,但是在小程序中仍然也无法获取到字体的高度。
这就完全超出我的认知和理解了,无奈之下只能联系小程序的开发者。
官方解答
以下解答的时间点:2022-05-26
小程序最低是按 chrome 66, safari 10 支持的
只能是做这种的最低适配。我们只能保证运行环境不会低于这个版本
无法保持一致的,本身运行环境就不同
模拟器基于node可以还可以用最新的js语法,但真机上就不一定可以
降级处理
既然目前(2022-05-26)小程序上无法获取到文本的高度,那么如何进行垂直居中计算呢?
以下是在 mac chrome 下测试 Helvetica 和 Arial 字体,从 10px 到 100px 分别计算 高度和宽度的比值。
字体 | 10px | 20px | 30px | 40px | 50px | 60px | 70px | 80px | 90px | 100px |
---|---|---|---|---|---|---|---|---|---|---|
Helvetica | 1.18 | 1.08 | 1.11 | 1.1 | 1.12 | 1.1 | 1.11 | 1.1 | 1.1 | 1.1 |
Arial | 1.12 | 1.12 | 1.12 | 1.12 | 1.12 | 1.12 | 1.12 | 1.12 | 1.12 | 1.13 |
以下是在 mac safari 下测试 Helvetica 和 Arial 字体,从 10px 到 100px 分别计算 高度和宽度的比值。
字体 | 10px | 20px | 30px | 40px | 50px | 60px | 70px | 80px | 90px | 100px |
---|---|---|---|---|---|---|---|---|---|---|
Helvetica | 1.2 | 1.15 | 1.16 | 1.15 | 1.16 | 1.15 | 1.15 | 1.15 | 1.15 | 1.15 |
Arial | 1.1 | 1.1 | 1.1 | 1.1 | 1.12 | 1.11 | 1.11 | 1.11 | 1.11 | 1.12 |
以下是在 iOS safari 下测试 Helvetica 和 Arial 字体,从 10px 到 100px 分别计算 高度和宽度的比值。
字体 | 10px | 20px | 30px | 40px | 50px | 60px | 70px | 80px | 90px | 100px |
---|---|---|---|---|---|---|---|---|---|---|
Helvetica | 1.3 | 1.25 | 1.2 | 1.2 | 1.18 | 1.18 | 1.17 | 1.17 | 1.16 | 1.17 |
Arial | 1.3 | 1.2 | 1.16 | 1.15 | 1.14 | 1.13 | 1.12 | 1.12 | 1.13 | 1.13 |
详细测试代码详见:codepen
因此,对于无法获取到metrics.fontBoundingBoxAscent
属性值的时候,可以使用metrics.width * 1.15
做一个降级处理。
修改后的代码如下:
1 | context.font = '50px Helvetica'; |
最终验证,修改后的代码在小程序上可以“近似”做到垂直居中。