從畢業以來,基本就一直在做移動端,但是一直就關於移動端的開發,各種適配問題的解決,在日常搬磚中處理了就過了,也沒有把東西都沉澱下來,覺得甚是寒顏。現就一個小bug,讓我們來了解一下我們天天都在用的emoji,對於開發來說,是一個怎麼樣的存在。
背景
之前在做一個留言功能時,發現在其中一臺安卓5.0的手機上,輸入emoji糊掉了,成了如下這樣的情況
這是skr啥玩意兒呀,怎麼看上去像某白色幼蟲。
與是我又試了好幾個手機,ios都沒有問題,甚至一臺安卓機中之霸(安卓4.0),隨便進個頁面都要載入十幾秒的手機都沒有問題,是亂碼了嗎?
為啥emoji會出現亂碼呢?相信很多人都遇到過關於emoji的問題,比如輸入emoji,傳給後端,再經過一系列操作後從介面中取到後端返回的emoji字元就亂了。又比如為了限制輸入字數,給字元做截斷時出現的問題。
初步懷疑是編碼問題,那我們就來看看emoji究竟是何方神聖。
emoji的歷史
emoji對於我們來說並不陌生,我們很早就開始接觸它了。emoji這個詞來源於日語裡的“絵文字”(假名為“えもじ”,讀音即emoji)。它是1999年,當時還在日本無線運營商NTT DoCoMo工作的Shigetaka Kurita(慄田穣崇)發明的。
emoji的編碼
emoji雖然看上去是一個有顏色有形狀的表情,但它屬於計算機中的字元。在計算機中,我們把文字、標點符號、圖形符號、數字等統一稱為字元,由字元組成的集合,我們稱為字符集。為了讓計算機識別字符集裡的字元,我們設計了一套字符集編碼規則,比如ASCII碼,由於ASCII只規定了128個字元的編碼,隨著計算機的發展,人們意識到這些編碼顯然是不夠的,為了統一世界上的所有字元,誕生出了Unicode字符集,而emoji字元就是Unicode字符集中的一部分。
Unicode
Unicode從0開始,為每個符號指定一個編號,稱做”碼點”,如U+0000,U+表示緊跟在後面的十六進位制數是Unicode的碼點。Unicode只規定了每個字元的碼點,到底用什麼樣的位元組序表示這個碼點,就涉及到編碼方法,比如我們html上常用的UTF-8。關於不同的編碼方法怎麼表示Unicode,以及JavaScript是怎麼處理Unicode,這裡就不詳細闡述了,可參考Unicode與JavaScript詳解 連結地址:http://www.ruanyifeng.com/blog/2014/12/unicode.html
所以emoji作為unicode,那在計算機上是怎麼顯示的?
之前我在一微信群裡@我一朋友,結果出現了下面的情況。
@符號跑右邊去了,當時覺得很奇怪,後來瞭解到,這是阿拉伯文,因為阿拉伯文的書寫規則是從右向左,所以@符號跑到右邊去了,可見微信對不同unicode字元排版做的相容還挺好。再比如這幾個字元,熱҈得҈字҈出҈汗҈了҈。
這就涉及到了複雜文字編排(Complex text layout,縮寫:CTL)。要求複雜文字編排以適當顯示的書寫系統稱為複雜文字,比如阿拉伯文字、婆羅米系文字的天城文、泰文等。
拿泰文來說,根據拼寫規則,泰文可形象地分為鞋子字元、主體字元、帽子字元、聲調字元等。泰文的每個基本字元對應一個unicode碼,人們在輸入多個基本字元時,新輸入的字元與之前的字元做匹配,如果可以組合,則這時前面的輸入就拼合成了一個泰文字元然後顯示出來。
薩瓦迪卡~
英文也是,我們在輸入英文時會習慣以空格來拆分前後單詞,你如果輸入一串連續的英文字母,計算機在識別上也會有困難。phpisthebestlanguageintheworld(手動滑稽臉)這句話就很有爭議!!
人為可以輕鬆識別一個泰文是否拼寫正確,但是計算機在顯示時就很難判斷。
像泰文這種特殊合成字元的本質,你無法避免人們在計算機上都會有哪些奇妙的創造。
於是乎,不同字元之間的組合,就誕生出了流行的顏文字:
ฅ՞•ﻌ•՞ฅ
ʕ•̼͛͡•ʕ-̺͛͡•ʔ•̮͛͡•ʔ
(⑉꒦ິ^꒦ິ⑉)
₊˚‧(๑σ̴̶̷̥́ ₃σ̴̶̷̀)·˚₊
୧(๑•̀⌄•́๑)૭✧
而字元的顯示,還有一個影響就是字型,在瀏覽器中,如果對應的編碼在字型檔案中為空,一般會展示成□□□□,這樣至少不會影響排版,但是unicode作為萬國碼實在太龐大了,在一些字型裡,對一些特殊字元還是會產生一些錯誤的排版,唉҈~真҈是惆҈悵~~
對於emoji來說,它雖然也是一種特殊字元,但它並不屬於複雜文字,並且我是通過移動終端規範輸入,排版也不會有什麼問題。我設定的font-family在其他手機上是好的也說明,這些字型對輸入的emoji也是支援的,出問題的終端上,非emoji的字型正常顯示,那暫時可以排除字型對emoji的影響了。
迴歸問題
到這裡,還沒有解決我的問題。本來以為是常見問題,比如資料提交時或者資料庫儲存的編碼問題。可是,我也沒傳給後端啊!我剛在自己的頁面上輸入顯示就成這樣了!
可惡,這個鍋甩不動了。還是得自己解決,我input框剛輸入,本地看到就亂了,看來還是自己的問題。
我一氣之下瘋狂亂點,發現不同的表情對應的這些小蟲長得還不一樣,於是,我決定把它放大看一看
這不就是表情麼,只是因為某些原因看上去被壓縮了。我的表情啊,你到底是經歷了什麼才變得如此面目全非。我一定要找到毀你容的真凶。
先分析一下表象,emoji的顯示被截斷、壓縮。為什麼被壓縮?迴歸場景,移動端切圖,那麼移動端的多終端適配,可不可能是問題的原因?
切圖是UI給的以iphone6的螢幕寬度為準的750px2倍視覺稿,組內方案選擇參考了手淘的flexible。具體原理和這次主題無關,我就不在這裡闡述了。關於移動端多端適配方案的原理詳細,可以參考 手淘H5頁面的終端適配
連結地址: https://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.html
那麼哪些程式碼是影響emoji縮放的程式碼呢?最先想到的是,我的emoji在輸入框裡面,設定了font-size,這個font-size的值是rem, 那會不會是某些安卓系統emoji對rem支援不好?於是我換成px,依然如此。
那麼頁面上還有哪兒還有會影響縮放呢?於是定位到了這裡。
-
1<meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0,width=device-width"/>
viewport是我們裝置螢幕上用來顯示網頁的區域,在移動端上,viewport一般都是大於瀏覽器可視區域。
理論上,移動端有三個viewport。
- layout viewport:移動瀏覽器為了讓所有網站正常顯示(包括那些PC的頁面),把預設的viewport設為了一個較寬值,這個值一般都是大於移動端可視區(比如iPhone 980px)。也就是document.documentElement.clientWidth
- visual viewport:代表瀏覽器可視區域的大小。也就是window.innerWidth
- ideal viewport:能完美適配移動裝置的viewport,使用者不需要縮放和橫向滾動條就能完美看到網頁內容,並且文字圖片,在不同解析度螢幕下顯示出來太小應該是差不多的(比如iPhone的ideal viewport寬度是320px)
關於各個裝置的ideal viewport 可以從這裡查詢,連結地址:http://viewportsizes.com/
所以我們利用meta標籤,設定viewport的寬度等於裝置的寬度,並且不允許使用者手動縮放。讓viewport的寬度等於裝置的寬度,這個應該就是我們想要的理想寬度。
實際上,只設定initial-scale=1,我們也能把當前的viewport寬度變成ideal viewport的寬度(這裡不考慮iphone下不同dpr的縮放),因為這個縮放就是相對於ideal viewport來進行縮放的。當同時設定了width與initial-scale=1,瀏覽器會選擇兩者中較大的那個值。
說了這麼多,那麼我的問題出在哪兒呢?猜想是不是該安卓版本對設定width和initial-scale會有一些意想不到的問題,於是我去掉了width=device-width,保留initial-scale=1等屬性,結果emoji竟然好了。
所以我遇到的情況就是,同時設定了width=device-width和initial-scale=1,會造成某些廠商手機的安卓5.0(目前只遇到這個)emoji被拉伸,去掉width=device-width,(不寫width=device-width也就是windows phone上的IE無論是橫豎屏都把寬度設為豎屏時ideal viewport寬度,個人覺得這個無傷大雅),至於為什麼會這樣,我暫時只能深入到這啦 (╥╯^╰╥)
結論
每一個emoji,就是一個Unicode字元,由統一碼聯盟(The Unicode Consortium)來投票選拔和公佈,世界各地的人們可以向聯盟提交 emoji 提案。而統一碼聯盟的 emoji 規範,只是定義了某個字元的語義,再由 Emojipedia 這個網站對 emoji 進行描述表達,最後允許大家按照對描述的理解,自由地去設計圖案。
所以不同的廠商以及不同的系統,甚至瀏覽器、瀏覽器版本以及系統字型等,對emoji的支援程度與相容性是不一樣的。比如同一個emoji笑臉表情,在ios和安卓上顯示的效果也不一樣。為了統一emoji表情,很多公司都有自己的一套emoji mapping,來做Unicode碼與emoji表情的對映。
碎碎念
移動端開發總會遇到各種問題,有時候做相容也會遇到無法完全相容兩頭的情況,這時只能放棄受眾更小,選擇相容影響面、嚴重性更大的方案了。在解決問題的有時候深究下去,也會收穫很多。