在開發(fā)中遇到了需要獲取一段文字的長(zhǎng)度的技術(shù)性需求,這篇文章將會(huì)講述我是如何解決這樣的問題的
瀏覽器環(huán)境下
解決方法一: SPAN標(biāo)簽
我最開始的解決方法是創(chuàng)建一個(gè)span元素,然后innerHTML設(shè)置文本在設(shè)置好樣式后插入到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
}
}

問題
-
不同瀏覽器的差異
不同瀏覽器獲取的高度寬高有一些差別
chrome:
imagefirefox:
image不知道為何
firefox的高總是比chrome高2px,但這個(gè)我們可以通過直接獲取傳入的fontSize作為高,這樣就可以統(tǒng)一了function getCharSize(char, style = {}){ let { fontSize = 14, fontFamily = "SimSun" } = style /*其他操作*/ return { width, height: fontSize } }image -
瀏覽器對(duì)字體大小的限制
chrome默認(rèn)最小字體為12px,基本是人盡皆知的
image這里可以使用scale的方式實(shí)現(xiàn)
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
優(yōu)點(diǎn)
兼容幾乎所有瀏覽器
缺點(diǎn)
-
會(huì)受一些潛在的全局樣式影響
image
解決方法二:Canvas measureText函數(shù)
除了使用span來(lái)獲取瀏覽器表現(xiàn)的大小這樣直接的方式以外,還有可以通過使用canvasAPI的CanvasRenderingContext2D.measureText()方式來(lái)快速獲取
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
}
問題
-
chrome瀏覽器存在BUG,如果canvas不在DOM樹上設(shè)置字體大小小于12px時(shí),字體大小會(huì)強(qiáng)制設(shè)置為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
代碼整理
下面給出優(yōu)化后的代碼
方法一: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計(jì)算
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
}
性能比較
數(shù)據(jù)為進(jìn)行10000次單字符計(jì)算
-
都需要插入DOM的情況下(方法二兼容chrome,且字體都為12px以下)
imageimageimage方法二效率比方法一快: 150%左右
-
方法二字體都為12px以上
imageimageimage方法二效率比方法一快: 1500%左右
從效率上來(lái)講,canvas效率是極高的,同時(shí)canvas還有還在制定的標(biāo)準(zhǔn),可以提供更加詳細(xì)的文本信息,chrome只需要去chrome://flags/開啟Experimental canvas features就可以提前使用該功能

參考資料
CanvasRenderingContext2D.measureText()
END
2018-03-31 完成
2017-12-13 立項(xiàng)












