浏览器JS计算字符宽高

在开发中遇到了需要获取一段文字的长度的技术性需求,这篇文章将会讲述我是如何解决这样的问题的

浏览器环境下

解决方法一: SPAN标签

我最开始的解决方法是创建一个span元素,然后innerHTML设置文本在设置好样式后插入到body中,读取宽高

function getCharSize(char, style = {}){
    let {
        fontSize = "12px",
        fontFamily = "SimSun"
    } = style

    let span = document.createElement("span")
    span.style.font = `${fontSize} ${fontSize}`
    span.style.lineHeight = fontSize
    span.innerHTML = str
    document.body.appendChild(span)
    let rect = span.getBoundingClientRect()
    let width = rect.width
    let height = rect.height
    document.body.removeChild(span)
    return {
        width,
        height
    }
}
image

问题

  1. 不同浏览器的差异

    不同浏览器获取的高度宽高有一些差别

    chrome:

    image

    firefox:

    image

    不知道为何firefox的高总是比chrome高2px,但这个我们可以通过直接获取传入的fontSize作为高,这样就可以统一了

    function getCharSize(char, style = {}){
        let {
            fontSize = 14,
            fontFamily = "SimSun"
        } = style
        /*其他操作*/
        return {
            width,
            height: fontSize
        }
    }
    
    image
  2. 浏览器对字体大小的限制

    chrome默认最小字体为12px,基本是人尽皆知的

    image

    这里可以使用scale的方式实现

    function getCharSize(char, style = {}){
        let {
            fontSize = 14,
            fontFamily = "SimSun"
        } = style
        
        /*其他操作*/
        let scale = fontSize / 20
        span.style.fontSize = `${20}px`
        span.style.transform = `scale(${scale})`
        span.style.display = "inline-block" //让scale生效
        /*其他操作*/
        return {
            width,
            height: fontSize
        }
    }
    
    image

优点

兼容几乎所有浏览器

缺点

  1. 会受一些潜在的全局样式影响

    image

解决方法二:Canvas measureText函数

除了使用span来获取浏览器表现的大小这样直接的方式以外,还有可以通过使用canvasAPI的CanvasRenderingContext2D.measureText()方式来快速获取

const ctx = document.createElement('canvas').getContext('2d')
function getCharSizeByCanvas(char, style = {}){
    let {
        fontSize = 14,
        fontFamily = "SimSun"
    } = style
    
    ctx.font = `${fontSize}px ${fontFamily}`
    let text = ctx.measureText(char)
    let result = {
        height: fontSize,
        width: text.width
    }

    return result
}

问题

  1. chrome浏览器存在BUG,如果canvas不在DOM树上设置字体大小小于12px时,字体大小会强制设置为12px

    
    //setfont before append
    const canvas1 = document.createElement('canvas')
    canvas1.width = 100
    canvas1.height = 100
    const ctx1 = canvas1.getContext('2d')
    ctx1.font = "8px Arial"
    ctx1.fillText(ctx1.font, 0, 50)
    document.body.appendChild(canvas1)
    
    //set font after append
    const canvas2 = document.createElement('canvas')
    canvas2.width = 100
    canvas2.height = 100
    const ctx2 = canvas2.getContext('2d')
    document.body.appendChild(canvas2)
    ctx2.font = "8px Arial"
    ctx2.fillText(ctx2.font, 0, 50)
    
    
    image

代码整理

下面给出优化后的代码

方法一:span

let span = document.createElement("span")
span.style.positon = "ablsolute"

function getCharSize(char, style = {}){
    let {
        fontSize = 14,
        fontFamily = "SimSun"
    } = style
    
    let scale = fontSize / 20
    span.style.fontSize = "20px"
    span.style.fontFamily = fontFamily
    span.style.lineHeight = "0"
    span.style.transform = `scale(${scale})`
    span.style.display = "inline-block"
    span.innerHTML = char
    document.body.appendChild(span)
    let rect = span.getBoundingClientRect()
    let width = rect.width
    document.body.removeChild(span)
    return {
        width,
        height: fontSize
    }
}

方法二:canvas计算

let canvas = document.createElement('canvas')
canvas.style.positon = "ablsolute"
let ctx = canvas.getContext('2d')

function getCharSizeByCanvas(char, style = {}){
    let {
        fontSize = 14,
        fontFamily = "Arial"
    } = style
    document.body.appendChild(canvas)
    ctx.font = `${fontSize}px ${fontFamily}`
    document.body.removeChild(canvas)
    let text = ctx.measureText(char) // TextMetrics object
    ctx.fillText(char, 50, 50)
    let result = {
        height: fontSize,
        width: text.width
    }
    return result
}

性能比较

数据为进行10000次单字符计算

  1. 都需要插入DOM的情况下(方法二兼容chrome,且字体都为12px以下)

    image

    image

    image

    方法二效率比方法一快: 150%左右

  2. 方法二字体都为12px以上

    image

    image

    image

    方法二效率比方法一快: 1500%左右

从效率上来讲,canvas效率是极高的,同时canvas还有还在制定的标准,可以提供更加详细的文本信息,chrome只需要去chrome://flags/开启Experimental canvas features就可以提前使用该功能

image

参考资料

CanvasRenderingContext2D.measureText()

TextMetrics

END

2018-03-31 完成

2017-12-13 立项

你可能感兴趣的:(浏览器JS计算字符宽高)