浏览器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 立项

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • <a name='html'>HTML</a> Doctype作用?标准模式与兼容模式各有什么区别? (1)、<...
    clark124阅读 3,453评论 1 19
  • 四年匆匆走过,走得太快,自己没跟得上脚步,总说自己很忙,忙来忙去,失去的想得到,得到的又失去了,放弃的也没来得及放...
    木子谭记阅读 224评论 0 0
  • 文/Nichole 文学/情感 这是一篇来自中国新闻周刊的热文,mark 一下。正好是这一刻我想对自己说的话。有些...
    翁萍阅读 328评论 0 0