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对象其他属性的含义。 ctxtextBaseline指定文字在垂直方向的对齐方式,文本基线的位置,之前那个基线(x的下沿)是标准的字母基线。

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

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

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

垂直居中

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

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

图片下方有空白区域

这段空白就是底线到行框的距离,行框的大小又取决于行高,所以设置line-heightfont-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;
}

总结

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

相关推荐
wordbaby15 小时前
前端进阶:小程序 Canvas 2D 终极指北 — 给图片优雅添加水印
前端·canvas
wordbaby16 小时前
小白也能看懂:小程序 Canvas 给图片添加水印的终极指南
前端·canvas
anOnion2 天前
构建无障碍组件之Switch Pattern
前端·html·交互设计
Kakarotto2 天前
Canvas 直线点击事件处理优化
javascript·vue.js·canvas
李剑一6 天前
大屏天气展示太普通?视觉升级!用 Canvas 做动态天气遮罩,雷阵雨效果直接封神
前端·vue.js·canvas
willow8 天前
html5基础整理
html
叶智辽9 天前
【Three.js多相机渲染】如何在同一场景里实现“画中画”效果
three.js·canvas
anOnion10 天前
构建无障碍组件之Radio group pattern
前端·html·交互设计
前端Hardy10 天前
HTML&CSS&JS:丝滑无卡顿的明暗主题切换
javascript·css·html
全栈老石11 天前
手写无限画布4 —— 从视觉图元到元数据对象
前端·javascript·canvas