canvas之measureText测量文本

前言

这篇文章讲的是关于canvas的一个方法:measureText,这个方法接受一个字符串参数,返回关于这个字符串的宽度和水平线到顶部或底部的距离等等。主要涉及的知识点有:获取文本宽度、文本在垂直方向的对齐方式、基线、行高。

获取字符串宽度

js 复制代码
<canvas id="canvas" width="200" height="200"></canvas>
<script>
    const canvas = document.getElementById('canvas');
    const context = canvas.getContext('2d');
    context.font = '16px Microsoft YaHei';
    console.log(context.measureText('我你他'));
</script>

打印出来是一个对象,其中的width属性就是传入的字符串的宽度。有2点需要注意一下,第一点是需要设置canvas中的文字font属性与需要测量的字符串的值一致。第2点是width值有一点点误差,我只测试了在Chrome中的情况,误差如下。

  • 中文,width是整数,无误差。
  • 英文,测量值略小于实际值0.01左右,实际值=测量值的四舍五入保留2位小数再向上取整保留2位小数。
  • 数字,测量值略小于实际值0.01左右,实际值=测量值的四舍五入保留2位小数再向上取整保留2位小数。 比如测量值是85.7109,实际值是85.72。测量值是62.1796875,实际值是62.19。

基线相关的基本概念

measureText的返回值是TextMetrics对象,它的属性除了width,还有一些是与基线、顶线相关的值,下面先了解一下相关概念。借用一下别人的图片。

  • 基线,x的下沿
  • 内容区,从顶线到底线的区域
  • 行高,内容区+上下空白区域的高度,等于相邻行的基线之间的距离
  • 行距,从上一行基线到下一行顶线的距离
  • 行内框,内容区+上下空白区域,它的高度就是line-height指定的高度
  • 行框,一行内多个字符串的行内框的最大值

接下来了解一下TextMetrics对象其他属性的含义。 ctx的textBaseline指定文字在垂直方向的对齐方式,文本基线的位置,之前那个基线(x的下沿)是标准的字母基线。

  • fontBoundingBoxAscent,从文本基线到行框顶部的距离
  • fontBoundingBoxDescent,从文本基线到行框底部的距离
  • actualBoundingBoxAscent,从文本基线到顶线的距离
  • actualBoundingBoxDescent,从文本基线到底线的距离
  • actualBoundingBoxLeft,从水平对齐方式的对齐点到行框最左边的距离
  • actualBoundingBoxRight,从水平对齐方式的对齐点到行框最右边的距离

总结,前4个属性暂不清楚有什么使用场景,后面2个属性可以用来计算文本宽度,MDN推荐用这2个值相加来获取倾斜字符串的绝对宽度。如果不是斜体,我推荐用width。

了解测量文本可以解决什么问题

垂直居中

最常见的就是一行中图片和文字如何垂直居中。 默认是基于基线对齐的,设置为基于中线就可以做到垂直居中了。

js 复制代码
.img {
    width: 30px;
    vertical-align: center;
}
.name {
    vertical-align: center;
}

图片下方有空白区域

这段空白就是底线到行框的距离,行框的大小又取决于行高,所以设置line-height或font-size为0即可。

measureText的应用场景

动态调整文字大小

js 复制代码
const getFontSize = (str, parentWidth) => {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  context.font = '16px Microsoft YaHei';
  const { width } = context.measureText(str);
  if (parentWidth - width < 0) {
    return 14;
  }
  return 16;
}

比如用户拖拽某个卡片大小时,或某个容器的字符串不固定长度时就可以这样来调整font-size。

绝对定位的不定长文本水平居中

js 复制代码
const getTextPos = (str, parentWidth) => {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  context.font = '16px Microsoft YaHei';
  const { width } = context.measureText(str);
  return (parentWidth - width) / 2;
}
textDom.style.left = getTextPos('abcdefg中文', parentDom.width) + 'px';

正常情况下,就算是不定长文本也可以设置text-align: center来水平居中。但,如果这段不定长文本是绝对定位的就需要计算一下应该设置的位置。

判断字符串会不会容器范围

js 复制代码
const isBeyond = (str, parentWidth) => {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  context.font = '16px Microsoft YaHei';
  const { width } = context.measureText(str);
  return width - parentWidth;
}

总结

需要获取文本宽度的情况可以考虑这个方法。

相关推荐
福兮说6 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
weixin_4482902510 小时前
书庐开发实战教学
javascript·css·html
Access开发易登软件1 天前
Access 导入 Markdown 表格:把文档里的数据写进表
前端·html·vba·markdown·access开发
web打印社区2 天前
C-Lodop 提示未准备好或 WebSocket 没准备好:先让本机服务起来
javascript·网络·websocket·网络协议·pdf·html
COOLMO研究AI2 天前
企业官网服务页的信息架构怎么设计:从用户问题到语义化 HTML
前端·css·html
web打印社区3 天前
HTML5 静默打印:前端页面怎么调用打印机且尽量不弹窗
前端·javascript·vue.js·pdf·html·html5
web打印社区4 天前
政务办事大厅:叫号小票、办理回执网页怎么静默出纸
前端·javascript·vue.js·pdf·html·政务
IMPYLH5 天前
HTML 的 <title> 元素
前端·javascript·html
杨超越luckly5 天前
上海社零数据:消费结构分化——1.66万亿盘子从+7.2%到-1.0%的十五年观察
html·agent·数据可视化·上海·社会消费品零售总额
miss6 天前
给规则引擎加一个拖拽画布:Vue Flow 单向投影 + 布局语义分离
javascript·vue.js·canvas