選單

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

本文嘗試講解 Emoji 的一些有趣的知識,來幫助我們更好的理解這個全世界通用的語言。在開啟之前,先看看你的 Emoji 考試能有幾分:

你知道不同的膚色 是怎麼實現的嗎你知道為什麼早期 iOS 總會被 Emoji 的問題弄出 crash 嗎你知道為什麼會有兩種 Emoji 嗎?︎ ️1

從下面開始,為了最好的理解,可以開啟你的瀏覽器,跟我寫點 JavaScript‍,一起看看 Emoji 的世界。

定義

這裡我們先統一 Emoji 的定義,到底哪些東西算 Emoji?

iPhone 輸入法裡點選 按鈕

微信、抖音的表情

Telegram 在輸入 Emoji 的時候,會有表情包聯想。傳送 Emoji 後還會有動畫

第一個場景是 Emoji 肯定是毋庸置疑的。它們雖然像圖片,但是你複製粘貼後就是能隨便發。

而其他都不算。這裡我們做一個定義:

只有能被作業系統定義的編碼才有可能是 Emoji。

怎麼理解這段話?

微信的表情你在輸入後,當你退出到列表介面,或者嘗試複製它,只能得到類似

[微笑]

的字元,而不是我們看到的微笑表情。所以它們本質上文字,只是微信對它做了圖片替換,並保證在刪除表情的時候也能直接刪除 4 個字元,從而讓表現上看上去就和文字類似。

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

相信你也遇見過這個場景,朋友發了一個訊息,你在通知上看到的是一個 ,你正奇怪這個朋友從來不發 emoji 的時候,點進去一看其實就是微信的

[微笑]

表情。這裡為什麼會這樣呢?

原因是通知中心的文字裡只能是純文字,所以微信不能對實際上是

[微笑]

的字元做替換,就採用了對這種字元做 Emoji 替換的方式,讓其在通知中心的表現不會像有 bug。但需要吐槽的是,微信的微笑應該和 這個對映才對~

Telegram 是個非常優秀的社交軟體,稱他是 IM 的天花板完全不為過。所以你在 Telegram 中發 Emoji 可能會得到一個它們進行定製的動畫,特別有喜感。但你將 Emoji 發出去後,

它被傳播的形式並不是 Emoji

,而是一個帶有動畫的圖片。只不過這個圖片你在複製的時候,得到的還是 Emoji 本身。

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

Telegram 對 Emoji 做的動畫

綜上我們看到的結論為:

Emoji 就是文字

,它能被複制貼上,只是看上去是個彩色圖片,也只有文字才能被編碼。而文字和圖片最重要的區別就是位元組數量,最大的 Emoji 被編碼出來後不會超過 30 個位元組,而圖片往往要幾百位元組起。那 Emoji 為普通文字的區別又是什麼?

Emoji 在每個作業系統裡都會有一個專門的字型去定義,比如 Apple 就是

Apple Color Emoji。

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

大家熟知的粗體,斜體等文字的風格調整往往都是由字型檔案去做定義,它們描述了文字被加粗後的樣子。如果字型檔案缺失了這個資訊的話,作業系統可能回嘗試去模擬,也可能就直接忽略。所以這意味著:Emoji 無法被加粗

我們在劃定好 Emoji 的範圍後才能進一步討論,接下來先看看它的起源。

起源

E

(え)

mo

(も)

ji

(じ)—絵文字,其中

e

是 繪,

moji

代表文字的意思,最早起源於日本的 20 世紀 90 年代,由軟銀推出。

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

接著的事情大家都知道了,iPhone 面世,為了佔領日本市場(我有一億人需要用 Emoji 呢),就合作將其弄了進去。並隨著 iPhone 的熱度提升,Emoji 也開始世界範圍內大火 。接著 Emoji 被納入 Unicode,大部分 Emoji 都有了自己的碼位,成為一個事實上的標準。這樣消費者也不用擔心 iPhone 手機發的 Emoji 安卓看不到了。

但在當時那個年代,大部分手機螢幕並不是彩色的,所以一開始很多 Emoji 其實是這個樣子的:

︎︎,它們和 ️️ 又有什麼區別?

NOTE:在實際測試的時候我發現少數派在展示這種 Emoji 的時候,在 Safari 和 Chrome 上表現也有差別,可能會有展示問題。

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

我們以 ︎ 為例子。在最開始的時候,︎ 的 Unicode 編碼應該是 1f170

但之後螢幕開始變成彩色,於是定義一套彩色的字元開始有必要。但直接將文字形式的 Emoji 替換成彩色形式可能會對之前的表達帶來誤解,所以為了相容這種舊形式,採取了一種比較「奇怪」的方式來獲得圖片形式——Variation Selector。

Unicode 中定義了很多不同版本的 Variation Selector,我們只要記住對應 Text 的版本是 15,對應 Emoji 的版本是 16,後文將分別簡寫為 VS-15(0xfe0e),VS-16(0xfe0f)。於是︎和️的實際編碼分別是 0x1f170 0xfe0e 和 0x1f170 0xfe0f。

如果你感興趣的話,可以開啟瀏覽器的控制檯,輸入看看效果。

String。fromCodePoint(0x1f170, 0xfe0e)String。fromCodePoint(0x1f171, 0xfe0f)

所以後續的 Emoji 輸入法中,都會刻意新增 VS 來做區分。但之前這些沒加的文字應該怎麼處理?

Unicode 建議針對這些沒有 VS 符號的字元,採用 VS-15 作為預設,意味著 1f170 應該渲染成文字形式。但是 Apple 可能是為了秀自己的 Emoji 字型,並沒有遵守這個標準。所以在不同平臺上會有不同效果,比如這個 ️

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

如果你用過飛書的話,可能就體驗過系統輸入法給你的建議是圖片形式的 Emoji,但是傳送出去後就變成文字模式了。就是因為輸入法建議的 Emoji 丟了 VS,所以兩邊的軟體具有不同的解釋方式

旗幟問題

接著根據這個 VS-16,讓我們來看看 iOS 10 時期出現的一個 bug:

簡單點講就是一旦收到某人傳送過來的含有”️‍0”的短訊息,將會導致裝置宕機,需要重啟手機才能解決。

想要解釋清楚這個 bug,先要了解一下旗幟的規則。

我們先記住一個原則,Unicode 非常摳門,為了節省碼位它們什麼事情都幹得出來。你想想,世界上,那麼多國家。國家內部可能還有一些地區,這些地區也有自己的旗幟。如果一個個編碼顯得過於奢侈和複雜。舉個例子,假設 Unicode 先給國旗放 500 個碼位,然後現有國家用掉了 300 個。未來又有一些奇怪的旗幟也想進去湊數,但這樣可能導致剩下的 200 個也不夠放,那又需要臨時開一個區域放新的碼位。針對這個問題,Unicode 也提出了一種解決方案,對氣質做了分類

最簡單的旗幟:️。這些旗幟都有獨立的碼位,不過為了相容早期,可能會跟著一個 VS-16。國旗—。地區旗幟—。特殊旗幟—️‍‍☠️

國旗

它們是由專門的 Regional Indicator Symbol(RIS) 組合而來,從 A 到 Z,碼位分別是從 1f1e6 到 1f200,光是兩兩組合就有 26 * 26 中方案。開啟控制檯輸入

Array。from({ length: 26 }, (_, i) => String。fromCodePoint(0x1f1e6 + i))。join(‘,’)

就能看到所有的字元。為什麼要用

join(‘,’)

而不是

join(‘’)

?試試看

所以就是 放在一起,同樣的的就是 。

如果你對這種兩個字元放在一起,會變成新的字元的特性不是很熟悉的話,可能會認為這是 Emoji 的特性,但其實這種被稱為 Combining Character 的技術很早就已經被支援。

最常見的使用場景就是拉丁語和音調符號的組合。比如 é 有一個單獨的碼位 e9,但是它也可以由 e 和 ´ 組合,它們的碼位分別是 65 和 301。所以如果你在瀏覽器輸入

‘\u{65}\u{301}’

也能得到 é。但是像 JS 這種相對落後的語言並不會認為

‘\u{65}\u{301} === \u{e9}’

true

,反觀 Swift 這種相對現代的語言,則對這個特性有了良好的支援。

地區旗幟

我不太懂政治,不太清楚像英格蘭和蘇格蘭這兩個地方不是國家(世界盃都是兩支隊伍),但是它們都有自己的旗幟。這裡的組合邏輯就很複雜了,首先需要一個 跟上 ISO 定義的 RIS,再跟上一個 Cancel Tag(007f)*。像這裡英格蘭的 RIS 是 *GBENG,可能是 Great Britain EnGLand 的縮寫。換句話說,一個就佔了 7 個碼位,比直接寫「英格蘭」浪費多了

彩虹旗

這些旗幟本質上是透過 Emoji 組合起來的。比如彩虹旗就是 ️+ZWJ+,‍☠️是+ZWJ+☠️。ZWJ(Zero Width Joiner) 可以理解成 Emoji 膠水,用來粘合一些用於特殊意義的 Emoji,以達到節省碼位的目的,它們被稱之為 Emoji ZWJ Sequence,畢竟誰能知道 10 年後,我們手機裡有多少個 Emoji 呢?插一句題外話,當你發現專案中

if (result == ‘’)

分支怎麼都不為真的時候,可能就是有人在程式碼裡下毒了。

所以,回到我們上面提到 iOS 10 曾經的一個 bug,表面上是️0這種組合,如果直接輸入這個幾個符號並不會有什麼問題,因為它們的對應的編碼是

1f3f3, fe0f, 30, 1f308

。而會讓機器的 Crash 的資訊對應的編碼 1f3f3, fe0f, 200d, 30, 1f308—

其中多出來的

200d

就是 ZWJ

。所以說,造成這個漏洞的原因可能是:1f3f3, f30f, 200d 同時出現後,系統預期 200d 後面的字元可以組合一個新的 Emoji。但是透過人為新增的 0,過於想當然的系統就 Crash 了。

接著我們繼續展開 ZWJ,看看有哪些 Emoji 是基於這個來創造的。

Emoji ZWJ Sequence

這個的使用場景除了上面提到的物體之間的組合之外,更多的還是人之間的組合。比如和特殊的物品組合在一起,變成了某個專業人士。和組合在一起,在一起, 在一起,都能組合家庭。還有一些虛幻的人物,比如‍♀️,‍♀️這些,從 Emoji 12 開始,出於政治正確的目的,都將這些虛幻人物預設設定為「中性」,並透過 ZWJ 拼接 ♂️和 ♁️。最讓我覺得有趣的還是愛情相關的符號:‍❤️‍‍ ‍❤️‍。

下面我們來詳細討論這些

專業人員

‍‍‍‍,這四個 Emoji 對應的分別是女歌手、女教師、白面板的女技師、女大學生。根據 Emoji 的規則就是:

‍: + ZWJ +

‍: + ZWJ +

‍: + ZWJ +

‍: + ZWJ +

注意到基本公式就是 性別 Emoji + ZWJ + 一個能表示該專業人員的典型物體。雖然在學校的女人不一定僅僅是老師,還可能是家長、學生,但是一個老師由「女人+ 學校」組成我們很容易記憶。

其實透過這個規律我們可以看到很有意思的效果——

‘’ + ‘\u200d’ + ‘’ // ‍‘‍’。replace(‘’, ‘’) // ‍

引用 Twitter 上的一幅圖:

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

虛構人物

我嘗試儘量列出所有虛擬人物:

超級英雄:‍♂️‍♀️

超級反派:‍♂️‍♀️

法師:‍♂️‍♀️

仙子: ‍♂️‍♀️

吸血鬼:‍♂️‍♀️

人魚:‍♂️‍♀️

精靈:‍♂️‍♀️

妖怪:‍♂️‍♀️

殭屍:‍♂️‍♀️

這些虛擬人物的 Emoji 飽含了深深的政治正確。首先,任何角色都有一個男性,也有一個女性在裡面,特別是美人魚還有男性這個完全忍不了,甚至在 iOS 15。4 中還有懷孕的男性 Emoji 出現 :)

但是呢,Emoji 標準還更進一步,它們又多了一個沒有性別的符號,這樣的改動看上去似乎是因為近幾年平權運動的興起,所以多了一個符號。

但是 Emoji 的組合公式也變了,相比之前女性角色是由 男性角色加上 ♂️ 符號,這種過於男權的表現。

現在改為一個沒有性別的 Emoji + ZWJ + ♂️或者 ♀️ 表示對應的性別組合。

[。。。‘‍♂️’]。map(a => a。codePointAt(0)。toString(16))// [“1f9dc”, “200d”, “2642”, “fe0f”] (4)

愛情

一開始對愛情的定義應該挺容易的:,男性和女性之間出現了個❤️。公式為 + ZWJ + ❤️ + ZWJ + 。秉承著包容的態度,Emoji 也對愛情的定義進行了擴容—‍❤️‍‍❤️‍

有了愛情的下一步就是 kiss,所以 ‍❤️‍‍ ‍❤️‍‍ 也自然少不了。Kiss 相比愛情的 Emoji,是在❤️ 後面接個ZWJ 和 ‍‍‍——Interesting。

有了愛情之後,自然就會組成家庭

Family

家庭最開始也挺簡單的,爸爸和媽媽加上幾個孩子的組合。一個,一個,一一,兩個,兩個,就五種——‍‍ ‍‍ ‍‍‍ ‍‍‍ ‍‍‍ 。

公式為 + ZWJ + + ZWJ + + ZWJ + ,你可以很容易驗證。

[。。。‘’]。join(‘\u{200d}’)

但是不知道從什麼時候開始,家庭的概念也被擴充了。有單親家庭,有同性家庭:‍‍‍‍‍‍‍。不過公式相比早期的家庭,僅僅是將 刪去一個,或者換成兩個同樣 Emoji,比如

[。。。‘’]。join(‘\u{200d}’)

當然,少不了無性別的符號:

非標準

比如微軟對 的拓展,‍ ‍。這些符號應該只能在 Windows 系統上看到效果,它們的樣子類似於圖片所示

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

面板和髮型

我們現在已經知道 Emoji 是如何透過組合不同的字元來得到新的 Emoji,而這裡的魔力就是 ZWJ。上面提到的規則就涵蓋了我們在 Emoji 鍵盤中絕大多數 Emoji 的組成規則。接著,我們再來看看長按 Emoji 鍵盤的表現。圖片中我以一個捲髮男人的 Emoji 為例,長按後可以得到另外的 5 種顏色。

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

我記得在幾年前,iOS 剛剛支援展示這些面板的時候,大家認為預設的顏色代表了「黃種人」,有點點自豪感,但是似乎又過於黃了—這些老外是不是沒看過我們亞洲人啊?之所以將基準顏色設計成這種黃色,僅僅是因為世界上沒有任何人長這樣。而 Emoji 中的部分顏色已經具有含義了,比如綠色的代表了中毒,紅色代表了中暑,藍色代表冷死,所以黃色顯然是一個挺不錯的選擇。

這五種顏色又是基於什麼來判定的?有一種 Fitzpatrick(菲茨帕特里克) Scale 分類法,就被 Emoji 面板系統採用,雖然最初是為了評估不同膚色的人對紫外線的反應程度。只不過相比 Fitzpatrick Scale 的 6 種的分類,Emoji 僅僅引入了 5 種,它將第一種和第二種合併了,因為這樣會看上去更真實。

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

所以在 Emoji 中表示顏色的方式就是人物或者人物器官,加上膚色的 Emoji。它們分別是

,碼位對應:1f3fb, 1f3fc, 1f3fd, 1f3fe, 1f3ff。

需要強調的是,膚色和下面將要提到的髮型,在和支援的 Emoji 組合在一起的時候,是不需要 ZWJ 的,所以 + 就是 。

可能是工作量的問題,家庭,愛情這些 Emoji 還沒有來得及支援膚色。反而是的 Emoji 最先支援了多種膚色的排列組合。

接著,除了面板之外,還有髮型。紅髮 捲髮 禿頭以及白髮,它們的碼位分別是 1f9b0, 1f9b1, 1f9b2, 1f9b3。我不太清楚為什麼會選用這四種髮型。

和面板一樣,滿足公式的 Emoji 並排放在一起就能看到組合成新的的 Emoji —白髮的女性 ‍。因為面板和髮型並不能直接透過 Emoji 鍵盤獲得,所以如果需要的話,可以透過執行下面的程式碼:

Array。from({ length: 5 }, (_, i) => String。fromCodePoint(i + 0x1f3fb))Array。from({ length: 4 }, (_, i) => String。fromCodePoint(i + 0x1f9b0))

總結

相信看到這裡,大家應該已經瞭解了 Emoji 的相關原理,也能對有些時候編輯 Emoji 的一些怪異行為坦然以對。比如下面膚色的符號: ,看上去有一個多出來的空格,但其實你就是刪不掉

如果又因為新的 bug 導致了 iOS 崩潰也不要嘲笑,因為這真的很難 :)

多元、多彩還能緊跟潮流:聊聊 Emoji 的組合與創造

在 Safari 上,英格蘭要刪好多次才能刪除乾淨

參考資料