轉(zhuǎn)自張譯文
從畢業(yè)以來(lái),基本就一直在做移動(dòng)端策治,但是一直就關(guān)于移動(dòng)端的開(kāi)發(fā)脓魏,各種適配問(wèn)題的解決,在日常搬磚中處理了就過(guò)了通惫,也沒(méi)有把東西都沉淀下來(lái)茂翔,覺(jué)得甚是寒顏。現(xiàn)就一個(gè)小bug履腋,讓我們來(lái)了解一下我們天天都在用的emoji檩电,對(duì)于開(kāi)發(fā)來(lái)說(shuō),是一個(gè)怎么樣的存在府树。
背景
之前在做一個(gè)留言功能時(shí)俐末,發(fā)現(xiàn)在其中一臺(tái)安卓5.0的手機(jī)上,輸入emoji糊掉了奄侠,成了如下這樣的情況?
與是我又試了好幾個(gè)手機(jī)卓箫,ios都沒(méi)有問(wèn)題,甚至一臺(tái)安卓機(jī)中之霸(安卓4.0)垄潮,隨便進(jìn)個(gè)頁(yè)面都要加載十幾秒的手機(jī)都沒(méi)有問(wèn)題烹卒,是亂碼了嗎闷盔?
為啥emoji會(huì)出現(xiàn)亂碼呢?相信很多人都遇到過(guò)關(guān)于emoji的問(wèn)題旅急,比如輸入emoji逢勾,傳給后端,再經(jīng)過(guò)一系列操作后從接口中取到后端返回的emoji字符就亂了藐吮。又比如為了限制輸入字?jǐn)?shù)溺拱,給字符做截?cái)鄷r(shí)出現(xiàn)的問(wèn)題。
初步懷疑是編碼問(wèn)題谣辞,那我們就來(lái)看看emoji究竟是何方神圣迫摔。
emoji的歷史
emoji對(duì)于我們來(lái)說(shuō)并不陌生,我們很早就開(kāi)始接觸它了泥从。emoji這個(gè)詞來(lái)源于日語(yǔ)里的“絵文字”(假名為“えもじ”句占,讀音即emoji)。它是1999年躯嫉,當(dāng)時(shí)還在日本無(wú)線運(yùn)營(yíng)商N(yùn)TT DoCoMo工作的Shigetaka Kurita(栗田穣崇)發(fā)明的纱烘。
emoji的編碼
emoji雖然看上去是一個(gè)有顏色有形狀的表情,但它屬于計(jì)算機(jī)中的字符祈餐。在計(jì)算機(jī)中擂啥,我們把文字、標(biāo)點(diǎn)符號(hào)昼弟、圖形符號(hào)、數(shù)字等統(tǒng)一稱(chēng)為字符奕筐,由字符組成的集合舱痘,我們稱(chēng)為字符集。為了讓計(jì)算機(jī)識(shí)別字符集里的字符离赫,我們?cè)O(shè)計(jì)了一套字符集編碼規(guī)則芭逝,比如ASCII碼,由于ASCII只規(guī)定了128個(gè)字符的編碼渊胸,隨著計(jì)算機(jī)的發(fā)展旬盯,人們意識(shí)到這些編碼顯然是不夠的,為了統(tǒng)一世界上的所有字符翎猛,誕生出了Unicode字符集胖翰,而emoji字符就是Unicode字符集中的一部分。
Unicode
Unicode從0開(kāi)始切厘,為每個(gè)符號(hào)指定一個(gè)編號(hào)萨咳,稱(chēng)做"碼點(diǎn)",如U+0000疫稿,U+表示緊跟在后面的十六進(jìn)制數(shù)是Unicode的碼點(diǎn)培他。Unicode只規(guī)定了每個(gè)字符的碼點(diǎn)鹃两,到底用什么樣的字節(jié)序表示這個(gè)碼點(diǎn)灯荧,就涉及到編碼方法啊犬,比如我們html上常用的UTF-8。關(guān)于不同的編碼方法怎么表示Unicode抄淑,以及JavaScript是怎么處理Unicode猛遍,這里就不詳細(xì)闡述了馋记,可參考Unicode與JavaScript詳解?鏈接地址:http://www.ruanyifeng.com/blog/2014/12/unicode.html
所以emoji作為unicode,那在計(jì)算機(jī)上是怎么顯示的螃壤?
之前我在一微信群里@我一朋友抗果,結(jié)果出現(xiàn)了下面的情況。
@符號(hào)跑右邊去了奸晴,當(dāng)時(shí)覺(jué)得很奇怪冤馏,后來(lái)了解到,這是阿拉伯文寄啼,因?yàn)榘⒗牡臅?shū)寫(xiě)規(guī)則是從右向左逮光,所以@符號(hào)跑到右邊去了,可見(jiàn)微信對(duì)不同unicode字符排版做的兼容還挺好墩划。再比如這幾個(gè)字符涕刚,熱?得?字?出?汗?了?。
這就涉及到了復(fù)雜文字編排(Complex text layout乙帮,縮寫(xiě):CTL)杜漠。要求復(fù)雜文字編排以適當(dāng)顯示的書(shū)寫(xiě)系統(tǒng)稱(chēng)為復(fù)雜文本,比如阿拉伯文字察净、婆羅米系文字的天城文驾茴、泰文等。
拿泰文來(lái)說(shuō)氢卡,根據(jù)拼寫(xiě)規(guī)則锈至,泰文可形象地分為鞋子字符、主體字符译秦、帽子字符峡捡、聲調(diào)字符等。泰文的每個(gè)基本字符對(duì)應(yīng)一個(gè)unicode碼筑悴,人們?cè)谳斎攵鄠€(gè)基本字符時(shí)们拙,新輸入的字符與之前的字符做匹配,如果可以組合阁吝,則這時(shí)前面的輸入就拼合成了一個(gè)泰文字符然后顯示出來(lái)睛竣。
薩瓦迪卡~
英文也是,我們?cè)谳斎胗⑽臅r(shí)會(huì)習(xí)慣以空格來(lái)拆分前后單詞,你如果輸入一串連續(xù)的英文字母求摇,計(jì)算機(jī)在識(shí)別上也會(huì)有困難射沟。phpisthebestlanguageintheworld(手動(dòng)滑稽臉)這句話(huà)就很有爭(zhēng)議J庹摺!
人為可以輕松識(shí)別一個(gè)泰文是否拼寫(xiě)正確验夯,但是計(jì)算機(jī)在顯示時(shí)就很難判斷猖吴。
像泰文這種特殊合成字符的本質(zhì),你無(wú)法避免人們?cè)谟?jì)算機(jī)上都會(huì)有哪些奇妙的創(chuàng)造挥转。
于是乎海蔽,不同字符之間的組合,就誕生出了流行的顏文字:
???????
???????-???????????
(???^???)
???(?σ????? ?σ????)·??
?(???????)??
而字符的顯示绑谣,還有一個(gè)影響就是字體党窜,在瀏覽器中,如果對(duì)應(yīng)的編碼在字體文件中為空借宵,一般會(huì)展示成□□□□幌衣,這樣至少不會(huì)影響排版,但是unicode作為萬(wàn)國(guó)碼實(shí)在太龐大了壤玫,在一些字體里豁护,對(duì)一些特殊字符還是會(huì)產(chǎn)生一些錯(cuò)誤的排版,唉?~真?是惆?悵~~
對(duì)于emoji來(lái)說(shuō)欲间,它雖然也是一種特殊字符楚里,但它并不屬于復(fù)雜文本,并且我是通過(guò)移動(dòng)終端規(guī)范輸入猎贴,排版也不會(huì)有什么問(wèn)題班缎。我設(shè)置的font-family在其他手機(jī)上是好的也說(shuō)明,這些字體對(duì)輸入的emoji也是支持的她渴,出問(wèn)題的終端上达址,非emoji的字體正常顯示,那暫時(shí)可以排除字體對(duì)emoji的影響了惹骂。
回歸問(wèn)題
到這里苏携,還沒(méi)有解決我的問(wèn)題做瞪。本來(lái)以為是常見(jiàn)問(wèn)題对粪,比如數(shù)據(jù)提交時(shí)或者數(shù)據(jù)庫(kù)儲(chǔ)存的編碼問(wèn)題∽芭睿可是著拭,我也沒(méi)傳給后端啊牍帚!我剛在自己的頁(yè)面上輸入顯示就成這樣了儡遮!
?我一氣之下瘋狂亂點(diǎn),發(fā)現(xiàn)不同的表情對(duì)應(yīng)的這些小蟲(chóng)長(zhǎng)得還不一樣暗赶,于是鄙币,我決定把它放大看一看
?這不就是表情么肃叶,只是因?yàn)槟承┰蚩瓷先ケ粔嚎s了。我的表情啊十嘿,你到底是經(jīng)歷了什么才變得如此面目全非因惭。我一定要找到毀你容的真兇。
先分析一下表象绩衷,emoji的顯示被截?cái)啾哪А嚎s。為什么被壓縮咳燕?回歸場(chǎng)景勿决,移動(dòng)端切圖,那么移動(dòng)端的多終端適配招盲,可不可能是問(wèn)題的原因低缩?
切圖是UI給的以iphone6的屏幕寬度為準(zhǔn)的750px2倍視覺(jué)稿,組內(nèi)方案選擇參考了手淘的flexible宪肖。具體原理和這次主題無(wú)關(guān)表制,我就不在這里闡述了。關(guān)于移動(dòng)端多端適配方案的原理詳細(xì)控乾,可以參考?手淘H5頁(yè)面的終端適配
鏈接地址: https://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.html
那么哪些代碼是影響emoji縮放的代碼呢么介?最先想到的是,我的emoji在輸入框里面蜕衡,設(shè)置了font-size壤短,這個(gè)font-size的值是rem, 那會(huì)不會(huì)是某些安卓系統(tǒng)emoji對(duì)rem支持不好?于是我換成px慨仿,依然如此久脯。
那么頁(yè)面上還有哪兒還有會(huì)影響縮放呢?于是定位到了這里镰吆。
<meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0,width=device-width" />
viewport是我們?cè)O(shè)備屏幕上用來(lái)顯示網(wǎng)頁(yè)的區(qū)域帘撰,在移動(dòng)端上,viewport一般都是大于瀏覽器可視區(qū)域万皿。
理論上摧找,移動(dòng)端有三個(gè)viewport。
layout viewport:移動(dòng)瀏覽器為了讓所有網(wǎng)站正常顯示(包括那些PC的頁(yè)面)牢硅,把默認(rèn)的viewport設(shè)為了一個(gè)較寬值蹬耘,這個(gè)值一般都是大于移動(dòng)端可視區(qū)(比如iPhone 980px)。也就是document.documentElement.clientWidth
visual viewport:代表瀏覽器可視區(qū)域的大小减余。也就是window.innerWidth
ideal viewport:能完美適配移動(dòng)設(shè)備的viewport综苔,用戶(hù)不需要縮放和橫向滾動(dòng)條就能完美看到網(wǎng)頁(yè)內(nèi)容,并且文字圖片,在不同分辨率屏幕下顯示出來(lái)太小應(yīng)該是差不多的(比如iPhone的ideal viewport寬度是320px)
關(guān)于各個(gè)設(shè)備的ideal viewport 可以從這里查詢(xún)如筛,鏈接地址:http://viewportsizes.com/
所以我們利用meta標(biāo)簽堡牡,設(shè)置viewport的寬度等于設(shè)備的寬度,并且不允許用戶(hù)手動(dòng)縮放。讓viewport的寬度等于設(shè)備的寬度杨刨,這個(gè)應(yīng)該就是我們想要的理想寬度悴侵。
實(shí)際上,只設(shè)置initial-scale=1拭嫁,我們也能把當(dāng)前的viewport寬度變成ideal viewport的寬度(這里不考慮iphone下不同dpr的縮放)可免,因?yàn)檫@個(gè)縮放就是相對(duì)于ideal viewport來(lái)進(jìn)行縮放的。當(dāng)同時(shí)設(shè)置了width與initial-scale=1做粤,瀏覽器會(huì)選擇兩者中較大的那個(gè)值浇借。
說(shuō)了這么多,那么我的問(wèn)題出在哪兒呢怕品?猜想是不是該安卓版本對(duì)設(shè)置width和initial-scale會(huì)有一些意想不到的問(wèn)題妇垢,于是我去掉了width=device-width,保留initial-scale=1等屬性肉康,結(jié)果emoji竟然好了闯估。
所以我遇到的情況就是,同時(shí)設(shè)置了width=device-width和initial-scale=1吼和,會(huì)造成某些廠商手機(jī)的安卓5.0(目前只遇到這個(gè))emoji被拉伸涨薪,去掉width=device-width,(不寫(xiě)width=device-width也就是windows phone上的IE無(wú)論是橫豎屏都把寬度設(shè)為豎屏?xí)rideal viewport寬度炫乓,個(gè)人覺(jué)得這個(gè)無(wú)傷大雅)刚夺,至于為什么會(huì)這樣,我暫時(shí)只能深入到這啦 (╥╯^╰╥)
結(jié)論
每一個(gè)emoji末捣,就是一個(gè)Unicode字符侠姑,由統(tǒng)一碼聯(lián)盟(The Unicode Consortium)來(lái)投票選拔和公布,世界各地的人們可以向聯(lián)盟提交 emoji 提案箩做。而統(tǒng)一碼聯(lián)盟的 emoji 規(guī)范莽红,只是定義了某個(gè)字符的語(yǔ)義,再由 Emojipedia 這個(gè)網(wǎng)站對(duì) emoji 進(jìn)行描述表達(dá)邦邦,最后允許大家按照對(duì)描述的理解安吁,自由地去設(shè)計(jì)圖案。
所以不同的廠商以及不同的系統(tǒng)圃酵,甚至瀏覽器柳畔、瀏覽器版本以及系統(tǒng)字體等馍管,對(duì)emoji的支持程度與兼容性是不一樣的郭赐。比如同一個(gè)emoji笑臉表情,在ios和安卓上顯示的效果也不一樣。為了統(tǒng)一emoji表情捌锭,很多公司都有自己的一套emoji mapping俘陷,來(lái)做Unicode碼與emoji表情的映射。
碎碎念
移動(dòng)端開(kāi)發(fā)總會(huì)遇到各種問(wèn)題观谦,有時(shí)候做兼容也會(huì)遇到無(wú)法完全兼容兩頭的情況拉盾,這時(shí)只能放棄受眾更小,選擇兼容影響面豁状、嚴(yán)重性更大的方案了捉偏。在解決問(wèn)題的有時(shí)候深究下去,也會(huì)收獲很多泻红。
更多文章
關(guān)注公眾號(hào)【grain先森】夭禽,回復(fù)關(guān)鍵詞 【18福利】,獲取為你準(zhǔn)備的年終福利谊路,更多關(guān)鍵詞玩法期待你的探索~