在開(kāi)發(fā)中遇到了需要獲取一段文字的長(zhǎng)度的技術(shù)性需求内狸,這篇文章將會(huì)講述我是如何解決這樣的問(wèn)題的
瀏覽器環(huán)境下
解決方法一: SPAN標(biāo)簽
我最開(kāi)始的解決方法是創(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
}
}
問(wèn)題
-
不同瀏覽器的差異
不同瀏覽器獲取的高度寬高有一些差別
chrome:
firefox:
不知道為何
firefox
的高總是比chrome
高2px拟赊,但這個(gè)我們可以通過(guò)直接獲取傳入的fontSize
作為高刺桃,這樣就可以統(tǒng)一了function getCharSize(char, style = {}){ let { fontSize = 14, fontFamily = "SimSun" } = style /*其他操作*/ return { width, height: fontSize } }
-
瀏覽器對(duì)字體大小的限制
chrome默認(rèn)最小字體為12px,基本是人盡皆知的
這里可以使用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 } }
優(yōu)點(diǎn)
兼容幾乎所有瀏覽器
缺點(diǎn)
-
會(huì)受一些潛在的全局樣式影響
解決方法二:Canvas measureText函數(shù)
除了使用span來(lái)獲取瀏覽器表現(xiàn)的大小這樣直接的方式以外吸祟,還有可以通過(guò)使用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
}
問(wèn)題
-
chrome瀏覽器存在BUG瑟慈,如果canvas不在DOM樹(shù)上設(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)
代碼整理
下面給出優(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以下)
方法二效率比方法一快: 150%左右
-
方法二字體都為12px以上
方法二效率比方法一快: 1500%左右
從效率上來(lái)講葛碧,canvas效率是極高的,同時(shí)canvas還有還在制定的標(biāo)準(zhǔn)过吻,可以提供更加詳細(xì)的文本信息进泼,chrome只需要去chrome://flags/
開(kāi)啟Experimental canvas features
就可以提前使用該功能
參考資料
CanvasRenderingContext2D.measureText()
END
2018-03-31 完成
2017-12-13 立項(xiàng)