深度剖析!瀏覽器字體渲染的秘密
說(shuō)到web性能,前端工程師很自然地反應(yīng)是yahoo的30+條優(yōu)化規(guī)則。這些規(guī)則可以將網(wǎng)頁(yè)加載從原來(lái)的幾秒甚至十幾秒較少到3s甚至1s以內(nèi)。當(dāng)一個(gè)完整界面展現(xiàn)在用戶眼前時(shí),內(nèi)容就通過(guò)不同的字體、圖片以及多媒體傳達(dá)給用戶。使用戶在1s內(nèi)看到網(wǎng)頁(yè)和使用戶留在網(wǎng)頁(yè)上幾分鐘甚至幾十分鐘同樣重要。字體作為內(nèi)容承載信息的重要部分,若使用不“適當(dāng)”的字體或者字體由于渲染等緣故對(duì)用戶不友好,則會(huì)(有可能)造成不必要的用戶流失。本文介紹瀏覽器的字體渲染,希望還未接觸字體渲染的同事能通過(guò)本文對(duì)字體渲染有所了解。
首先看下不同瀏覽器下的截圖:
很明顯地看出,Chrome35截圖中的非橫豎筆畫較IE11和Firefox30截圖中的有明顯的鋸齒。
理想的字體其邊緣過(guò)渡平滑,而在屏幕上顯示時(shí),需要將字體柵格化(rasterization)為一個(gè)個(gè)像素點(diǎn)。采用黑白(black and white)渲染無(wú)法體現(xiàn)字體的細(xì)節(jié)之處,會(huì)造成了邊緣呈現(xiàn)鋸齒狀(jagged)的不平滑。為了解決這個(gè)問(wèn)題,字體渲染引擎采用了以下方法進(jìn)行平滑(Antialiasing):灰階(grayscale)渲染、亞像素(sub-pixel)渲染。
渲染方法
灰階渲染是一種通過(guò)控制字體輪廓上像素點(diǎn)的亮度達(dá)到字體原始形狀的方法。
亞像素渲染利用了LCD屏(液晶屏)中每個(gè)像素是由R、G、B三個(gè)亞像素的顏色和亮度混合而成一個(gè)完整像素的顏色這一原理,將字體輪廓上的像素點(diǎn)由三個(gè)亞像素體現(xiàn)以達(dá)到原始形狀的方法。與灰階渲染相比,分辨率在垂直方向放大了三倍,因此渲染效果更好,但是所耗內(nèi)存也更多。因此在手機(jī)屏上,為了減少CPU開(kāi)銷,并未采用該字體渲染方法,而是采用的grayscale渲染。
操作系統(tǒng)中的渲染API
操作系統(tǒng)OS提供了支持不同的字體渲染方法的API。在windows下是GDI(Graphics Device Interface)和DirectWrite,OS X下是Quartz。
GDI分為GDI Grayscale和GDI ClearType。前者為灰階渲染API,后者是亞像素渲染API。由于GDI ClearType并未對(duì)字體進(jìn)行垂直方向的平滑,因此當(dāng)字體較大時(shí)會(huì)出現(xiàn)邊緣不平滑的情況。為了彌補(bǔ)GDI ClearType的不足,MS實(shí)現(xiàn)了DirectWrite API,它在GDI ClearType的基礎(chǔ)上增加了垂直方向的平滑。
但是!字體渲染的API都是由瀏覽器廠商自己選擇的!
使用同一顏色,感官上的顏色深淺為:黑白渲染>grayscale>sub-pixel。
Chrome35/36采用的是GDI ClearType,因此在字體較大時(shí)邊緣會(huì)出現(xiàn)毛刺,而FF30采用的DirectWrite則沒(méi)有此類問(wèn)題。如下圖所示:
?
根據(jù)以上兩圖可以發(fā)現(xiàn),Chrome的字體渲染效果沒(méi)有Firefox的好。為縮小Chrome與FF/IE的差異,一種方法是使用-webkit-text-stroke:0.5px;如果使用1px的話就有點(diǎn)粗了。
雖然該hack使Chrome下字體的邊緣有所光滑,但字體在webkit內(nèi)核瀏覽器中看上去“模糊”了。
常用字體在瀏覽器中的渲染情況
???
從使用Microsoft Yahei, Tahoma和Arial字體可以看出,在Chrome35中,由于轉(zhuǎn)換成GDI Grayscale渲染,49px的字比48px的邊緣光滑很多。字體較大時(shí),GDI Grayscale比GDI ClearType具有更好的渲染效果。
在實(shí)際中會(huì)遇到在如Tahoma/Arial字體下17px和18px的“字重”有明顯差別。這個(gè)現(xiàn)象涉及到字體設(shè)計(jì),簡(jiǎn)單地說(shuō)就是在瀏覽器渲染字體之前,字體本身會(huì)調(diào)整字型和筆畫所占像素。想了解更多細(xì)節(jié),請(qǐng)參考a closer look at TrueType hinting。
啟動(dòng)Chrome中的DirectWrite
在chrome://flags下啟用實(shí)驗(yàn)性DirectWrite字體渲染系統(tǒng):#enable-direct-write會(huì)使48px以下的字體均為sub-pixel渲染。僅適用于測(cè)試環(huán)境?。ㄐ柚貑hrome)
Chrome37 beta將該特性移除實(shí)驗(yàn)特性,并將在Chrome37中實(shí)現(xiàn)在windows下使用DirectWrite API對(duì)字體渲染,相信在Chrome37中以上提到的瀏覽器字體渲染差異會(huì)得到解決。
字體格式在不同瀏覽器下的渲染
font-smoothing: subpixel-antialiased | antialiased;
對(duì)應(yīng)為sub-pixel和grayscale。
已廢止!
font-weight: bold;
Chrome35, IE7/8:與normal字體渲染一致(包括數(shù)字和字母);
IE9+/FF30:grayscale,部分簡(jiǎn)單中文字體保持原有渲染。
font-style:italic;
Chrome35, IE7/8:與normal字體渲染保持一致;
IE9+/FF30:sub-pixel渲染。
opacity: 屬性值<1
Chrome35:sub-pixel渲染的字體“降級(jí)”為grayscale渲染;
IE9+/FF30:保持不變
transform 2D
transform: rotate(n);
n=90*k deg(k=整數(shù)), 保持未旋轉(zhuǎn)的字體渲染;
others,sub-pixel渲染。Chrome35在48px+為grayscale渲染。
transform: scale(x,y); 與字體大小m有關(guān)。以simsun為例,具體如下:
Chrome35:
x*y*m<12, Chrome35 sub-pixel渲染;
12<x*y*m<=48*48=2304 sub-pixel渲染; x*y*m>2304 grayscale渲染,12px-24px存在一定范圍的黑白渲染(tahoma是12px-16px)。
FF30、IE9+:
m*m*n<=99*99=9801 sub-pixel渲染; m*m*n>9801 grayscale渲染。
transform: skew(n);
n=90*k deg(k為整數(shù)), 保持未傾斜的字體渲染;
others, sub-pixel渲染。Chrome35在49px+為grayscale渲染,IE9+, FF30在100px+為grayscale渲染。
transform 2D & transition
運(yùn)動(dòng)中:以12px為例: 在Chrome35和FF30下,運(yùn)動(dòng)中的字體均是grayscale渲染;
運(yùn)動(dòng)結(jié)束:Chrome35, FF30, IE9+均為sub-pixel渲染。
從運(yùn)動(dòng)開(kāi)始到運(yùn)動(dòng)結(jié)束產(chǎn)生了兩次渲染變化(sub-pixel到grayscale,grayscale到sub-pixel)會(huì)出現(xiàn)“閃現(xiàn)”的現(xiàn)象。
Transform 3D
當(dāng)元素進(jìn)入GPU中渲染時(shí),在Chrome35+中的字體為grayscale渲染,IE11和FF30保持sub-pixel渲染不變。若transform值函數(shù)(如translate3d(), scale(), rotate()等)中的參數(shù)為非整數(shù),會(huì)導(dǎo)致字體模糊。在使用iScroll模擬滾動(dòng)的項(xiàng)目中會(huì)出現(xiàn)字體模糊。以下是對(duì)該問(wèn)題的一個(gè)還原:
-webkit-transform: translate3d(1.5px, 1.5px,0);
-webkit-transform: translate3d(1px, 1px,0);
Chrome 36中使用transform不需要-webkit-前綴了!
為了防止以上模糊情況的出現(xiàn),可以通過(guò)JS中的Math.round()/Math.ceil()/Math.floor()等函數(shù)使其為整數(shù)。
當(dāng)加入perspective()值時(shí),F(xiàn)irefox30渲染又有所不同。
transform: perspective(1px) translate3d(1.5px, 1.5px,0);在FF30中作用的元素為grayscale渲染。
transform: perspective(1px) translate3d(1px, 1px,0); 在FF30中作用的元素為sub-pixel渲染
E11均為sub-pixel渲染。
此情況下運(yùn)用opacity<1的聲明,IE10+為grayscale。
Chrome transform: perspective()對(duì)文檔后面元素的影響
Chrome35+,當(dāng)transform中聲明有perspective()時(shí),若文檔后面聲明transform的元素進(jìn)入該聲明作用的元素的layer-borders的區(qū)域時(shí),仍然會(huì)受到該聲明的影響。聲明作用元素的溢出部分同樣會(huì)對(duì)后面元素造成影響。合成渲染層內(nèi)部文字進(jìn)入GPU渲染。
啟動(dòng)Show composited layer borders,在Chrome中測(cè)試的結(jié)果截圖為:
總結(jié)
字體渲染與操作系統(tǒng)、瀏覽器、字體格式以及CSS屬性有關(guān)。本文重點(diǎn)羅列了一些在Windows Chrome中字體渲染與FF/IE的差異和對(duì)應(yīng)緩解/解決問(wèn)題的方案。Chrome在字體渲染上目前落后于IE/FF,但之前官方博客稱Chrome 37將實(shí)現(xiàn)在windows下使用DirectWrite API,Chrome在字體渲染方面將趕上IE/FF等瀏覽器,以上的差異性也將減小甚至消失。
原文地址:攜程UED
作者:fhz
- 目前還沒(méi)評(píng)論,等你發(fā)揮!