Javascript:根据元素宽度,给文本计算适合的字体大小

一、实现目标

动态计算字体大小,解决文字超出元素问题:

1、图一为默认字体大小99px,已超出元素宽度

2、图二为动态计算字体大小,使元素刚好可以放下文字

二、准备工作(网上查资料!😚会抄代码也很重要)

1、如何计算文本的宽度?
/**
 * 计算文本的宽度
 * @param {*} str  字符串
 * @param {*} font 所有字体属性 例:font:bold 15px arial,sans-serif;
 */
function getStringWidth(str, font) {
  const canvas = document.createElement("canvas")
  const context = canvas.getContext("2d")
  context.font = font
  return context.measureText(str).width
}

2、如何最快找到合适的字体大小(二分法)

二分查找算法:二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

三、具体实现

1、编写代码(直接复制代码吧,看第2点,😚!)

class fitFontText {
  /**
   * @param {*} dom DOM元素
   * @param {*} option 配置 {max: 字体最大限制, min: 字体最小限制}
   */
  constructor(dom, option = {}) {
    this.dom = dom
    this.option = option
    this.context = document.createElement("canvas").getContext("2d")
  }
  /**
   * 计算文本的宽度
   * @param {*} str  字符串
   * @param {*} font 所有字体属性 例:font:bold 15px arial,sans-serif;
   */
  getStringWidth(str, font) {
    this.context.font = font
    return this.context.measureText(str).width
  }
  /**
   * @param {*} str 字符串
   * @param {*} range 计算范围
   * @param {*} style 父元素样式 fw 字重 ff 字体样式 w 元素宽度
   * @param {*} first 首次计算
   */
  recursGetSize(str, { max = 1, min = 1 }, { fw = "normal", ff = "Arial,sans-serif", w = 0 }, first = true) {
    /* 最大值/最小值不允许一样 */
    if (min === max) return min
    /* 首次计算, 直接计算最大字体, 是否超出限制, 不超出直接采用最大值 */
    if (first) {
      const tw = Math.round(this.getStringWidth(str, `${fw} ${max}px ${ff}`))
      if (w >= tw || max <= 1) return max
    }
    /* 中间值 */
    const mid = Math.ceil((max + min) / 2)
    /* 最大与最小值中间隔的数值的个数,是奇还是偶(true偶false奇) */
    const even = (max - min - 1) % 2 === 0
    /* 计算取中间值(mid)时字符串的宽度 */
    const tw = Math.round(this.getStringWidth(str, `${fw} ${mid}px ${ff}`))
    /* 二分数值区间(interval) (奇数/偶数有不同区间)*/
    const interval = {
      less: [min, mid - 1],
      more: [even ? mid : mid + 1, max]
    }
    if (tw > w) {
      /* 字符串宽度大于最大限制 */
      /* (2)只剩一个值,无法继续二分,获得(最终结果) */
      if (interval.less[0] === interval.less[1]) return interval.less[1]
      /* 递归查找 */
      return this.recursGetSize(str, { max: interval.less[1], min: interval.less[0] }, { fw, ff, w }, false)
    } else if (tw < w) {
      /* 字符串宽度小于最大限制 */
      /* (3)只剩一个值,无法继续二分,需要判断采用最终值的字符串宽度是否超过最大限制(最终结果) */
      if (interval.more[0] === interval.more[1]) {
        const tw2 = Math.round(this.getStringWidth(str, `${fw} ${interval.more[0]}px ${ff}`))
        if (tw2 > w) {
          /* (3-1)(最终结果) */
          return mid
        } else {
          /* (3-2)(最终结果) */
          return interval.more[0]
        }
      }
      /* 递归查找 */
      return this.recursGetSize(str, { max: interval.more[1], min: interval.more[0] }, { fw, ff, w }, false)
    }
    /* (1)取中间值(mid), 字符串宽度等于最大限制(最终结果) */
    return mid
  }
  size(dom) {
    if (dom) this.dom = dom
    const { width, fontSize, fontFamily, fontWeight } = getComputedStyle(this.dom)
    const widthInt = parseInt(width)
    const fontSizeInt = parseInt(fontSize)
    const text = this.dom.innerText
    const maxSize = this.recursGetSize(
      text,
      {
        max: this.option.max ? this.option.max : fontSizeInt,
        min: this.option.min ? this.option.min : 1
      },
      { fw: fontWeight, ff: fontFamily, w: widthInt }
    )
    return maxSize
  }
}

2、使用方法

(1)基础用法

const dom = document.getElementById("test")
dom.style.fontSize = new fitFontText(dom).size() + 'px'

(2) 限制字体大小 (max 最大 min 最小) 单位px 

const dom = document.getElementById("test")
dom.style.fontSize = new fitFontText(dom, {max: 20, min: 16}).size() + 'px'

3、vue中的使用

在vue中用ref去获取dom后,设置font-size的方法实在太麻烦了(不会动态计算🤣)

解决方案: 可以使用自定义指令解决

const app = createApp({})

// 使 v-fit-font 在所有组件中都可用
app.directive('fit-font', (el) => {
  el.style.fontSize = `${new fitFontText(el).size()}px`
})

注册好后,就可以使用了

 <div v-fit-font class="text">
     发布文章333
 </div>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值