Emojiphp
公司的產品以前只有網頁端,並無提供emoji表情,以後將某個模塊整合到app中,裏面有個評論功能,在手機端能夠輸入emoji,顯示的時候是空白,說明數據庫並無存儲成功,查閱資料後得知emoji是四個字節,而mysql5.5.3前的版本utf8編碼最多隻支持3個字節。css
js解析emojihtml
先須要瞭解幾個概念,js的編碼方式、utf1六、unicodemysql
1.JavaScript語言採用Unicode字符集,可是隻支持一種編碼方法ucs-2jquery
2.utf16編碼git
utf16是ucs-2的超集github
3.Unicode只規定了每一個字符的碼點,到底用什麼樣的字節序表示這個碼點,就涉及到編碼方法sql
因爲JavaScript只能處理UCS-2編碼,形成全部字符在這門語言中都是2個字節,若是是4個字節的字符,會看成兩個雙字節的字符處理。JavaScript的字符函數都受到這一點的影響,沒法返回正確結果數據庫
emoji表情是由utf16編碼的,多是2個字節,也多是四個字節後端
這裏的解析我用的是twemoji庫,原理是將utf16編碼轉爲unicode的十六機制並以此十六進制做爲emoji圖片的命名
這裏的關鍵是如何將utf16轉爲unicode十六進制
將unicode轉爲utf16,官方給了公式
Unicode碼點轉成UTF-16的時候,首先區分這是基本平面字符(2字節),仍是輔助平面字符(4字節)。若是是前者,直接將碼點轉爲對應的十六進制形式,長度爲兩字節。
若是是輔助平面字符,Unicode 3.0版給出了轉碼公式。
H= Math.floor((c-0x10000)/0x400)+0xD800 //高位
L = (c-0x10000)%0x400+0xDC00 //低位
將utf16轉爲unicode則是知道H,L,求c,學過方程組的應該都會解答吧
給出上述轉化的函數
1 /*unicode編碼範圍 2字節0x0000-0xffff 2 四字節爲0x010000-0x10ffff 3 U+D800到U+DFFF 爲空段 4 因爲JavaScript只能處理UCS-2編碼,形成全部字符在這門語言中都是2個字節,若是是4個字節的字符,會看成兩個雙字節的字符處理 5 */ 6 function toCodePoint(unicodeSurrogates, sep) { 7 var 8 r = [], 9 c = 0, 10 p = 0, 11 i = 0; 12 while (i < unicodeSurrogates.length) { 13 c = unicodeSurrogates.charCodeAt(i++);//返回位置的字符的 Unicode 編碼 14 15 if (p) { 16 r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16)); //計算4字節的unicode 17 p = 0; 18 } else if (0xD800 <= c && c <= 0xDBFF) { 19 p = c; //若是unicode編碼在oxD800-0xDBff之間,則須要與後一個字符放在一塊兒 20 } else { 21 r.push(c.toString(16)); //若是是2字節,直接將碼點轉爲對應的十六進制形式 22 } 23 } 24 return r.join(sep || '-'); 25 }
emojipicker
頁面上選擇emoji表情,插入input,發送給後端時須要轉爲utf16
這裏我用的庫是jquery-emoji-picker,這裏遇到一個問題,它的css中背景圖片是datauri,我又須要兼容ie6,我須要將它的樣式文件重寫,並將圖片保存起來。若是數量少,我會手動改下,結果一看,855個,果斷寫腳本
1 <?php 2 function formatData($str){ 3 $data=array(); 4 $reg='/^\.emoji-([^\{]+)\s+\{background-size:100% !important; background-image: url\(\'(.+)\'\);\}/'; //匹配樣式 5 preg_match($reg, $str, $matches); 6 $data=array('filename'=>$matches[1].'.png','base64'=>substr($matches[2],22),'name'=>$matches[1]); 7 return $data; 8 } 9 function basetopng($base64,$filename){ 10 $str=base64_decode($base64); 11 file_put_contents('images/'.$filename, $str); 12 } 13 $css_file = 'emojipicker.css'; 14 $start = 0; // 從第0行開始讀取 15 $num = 855; // 讀取855行 16 $data = array(); 17 $str='';//生成css文件 18 $spl_object = new SplFileObject($css_file, 'rb'); 19 $spl_object->seek($start); 20 while ($num-- && !$spl_object->eof()) { 21 $tmp = $spl_object->fgets(); 22 $tmpData=formatData($tmp); 23 $filepath='../images/'.$tmpData['filename']; 24 $str.=".emoji-{$tmpData['name']} { background-size:100% !important; background-image: url('/Public/plugin/emojipicker/images/{$tmpData['filename']}');}\n"; 25 $spl_object->next(); 26 } 27 file_put_contents('emojipicker.ff.css', $str); 28 29 ?>
點擊icon得到emoji的name,將name轉化爲‘<微笑>’字樣插入input,提交給後臺的時候再將'<微笑>'字樣轉化爲utf16(先轉化爲unicode,在轉化爲utf16)
我又跑去微信界面盜了點資源過來,領導說作成微信相似就行了,原來的jquery.emojipicker.a.js中的數據結構是
{
"name": "sunny",
"unicode": "2600",
"shortcode": "sunny",
"description": "BLACK SUN WITH RAYS",
"category": "thing"
}
我須要給它加點東西,變成這樣
{
"name": "sunny",
"unicode": "2600",
"shortcode": "sunny",
"desc": "<太陽>",
"title": "太陽",
"category": "thing"
}
而微信的數據結構是這樣 {"<太陽>" : "2600"},應該怎麼對應,unicode與wx的value相等,以這個爲基準
1 var a=[],//須要的微信表情unicode 2 wxemojis=window.gQQFaceMap, 3 b=[];//格式化數據,添加desc,title 4 for(var i in wxemojis){ 5 if(wxemojis[i].length>3){ 6 if(a.indexOf(wxemojis[i]) < 0){ 7 a[a.length]=wxemojis[i]; 8 b[b.length]={ 9 'unicode':wxemojis[i], 10 'desc':i, 11 'title':i.replace(/(<|>)/g,'') 12 }; 13 } 14 } 15 } 16 var myEmojis=[];//我須要的表情數組 17 for(var i in emojis){ 18 var tmpIndex=a.indexOf(emojis[i].unicode.toLowerCase()); 19 if(tmpIndex > -1){ 20 myEmojis[myEmojis.length]={ 21 "name":emojis[i].name, 22 "unicode":emojis[i].unicode, 23 "shortcode":emojis[i].shortcode, 24 "desc":b[tmpIndex].desc, 25 "title":b[tmpIndex].title, 26 "category":emojis[i].category 27 } 28 } 29 } 30 console.log(JSON.stringify(myEmojis));
生成了本身的表情數組。
將中文字樣轉爲utf16,傳給後端
1 function toUnicode(code) { 2 var codes = code.split('-').map(function(value, index) { 3 return parseInt(value, 16); 4 }); 5 return String.fromCodePoint.apply(null, codes); 6 } 7 8 if (!String.fromCodePoint) { 9 // ES6 Unicode Shims 0.1 , © 2012 Steven Levithan http://slevithan.com/ , MIT License 10 String.fromCodePoint = function fromCodePoint () { 11 var chars = [], point, offset, units, i; 12 for (i = 0; i < arguments.length; ++i) { 13 point = arguments[i]; 14 offset = point - 0x10000; 15 units = point > 0xFFFF ? [0xD800 + (offset >> 10), 0xDC00 + (offset & 0x3FF)] : [point]; 16 chars.push(String.fromCharCode.apply(null, units)); 17 } 18 return chars.join(""); 19 } 20 } 21 function htmlEncode(a) { 22 return a && a.replace ? a.replace(/&/g, "&").replace(/\"/g, """).replace(/</g, "<").replace(/>/g, ">").replace(/\'/g, "'") : a 23 } 24 function afterEncodeEmoji(str){ 25 var faceMap={"<笑臉>":"1f604","<開心>":"1f60a","<大笑>":"1f603","<熱情>":"263a","<眨眼>":"1f609","<色>":"1f60d","<接吻>":"1f618","<親吻>":"1f61a","<臉紅>":"1f633","<露齒笑>":"1f63c","<滿意>":"1f60c","<戲弄>":"1f61c","<吐舌>":"1f445","<無語>":"1f612","<得意>":"1f60f","<汗>":"1f613","<失望>":"1f640","<低落>":"1f61e","<呸>":"1f616","<焦慮>":"1f625","<擔憂>":"1f630","<震驚>":"1f628","<悔恨>":"1f62b","<眼淚>":"1f622","<哭>":"1f62d","<破涕爲笑>":"1f602","<暈>":"1f632","<恐懼>":"1f631","<心煩>":"1f620","<生氣>":"1f63e","<睡覺>":"1f62a","<生病>":"1f637","<惡魔>":"1f47f","<外星人>":"1f47d","<心>":"2764","<心碎>":"1f494","<丘比特>":"1f498","<閃爍>":"2728","<星星>":"1f31f","<歎號>":"2755","<問號>":"2754","<睡着>":"1f4a4","<水滴>":"1f4a6","<音樂>":"1f3b5","<火>":"1f525","<便便>":"1f4a9","<強>":"1f44d","<弱>":"1f44e","<拳頭>":"1f44a","<勝利>":"270c","<上>":"1f446","<下>":"1f447","<右>":"1f449","<左>":"1f448","<第一>":"261d","<強壯>":"1f4aa","<吻>":"1f48f","<熱戀>":"1f491","<男孩>":"1f466","<女孩>":"1f467","<女士>":"1f469","<男士>":"1f468","<天使>":"1f47c","<骷髏>":"1f480","<紅脣>":"1f48b","<太陽>":"2600","<下雨>":"2614","<多雲>":"2601","<雪人>":"26c4","<月亮>":"1f319","<閃電>":"26a1","<海浪>":"1f30a","<貓>":"1f431","<小狗>":"1f429","<老鼠>":"1f42d","<倉鼠>":"1f439","<兔子>":"1f430","<狗>":"1f43a","<青蛙>":"1f438","<老虎>":"1f42f","<考拉>":"1f428","<熊>":"1f43b","<豬>":"1f437","<牛>":"1f42e","<野豬>":"1f417","<猴子>":"1f435","<馬>":"1f434","<蛇>":"1f40d","<鴿子>":"1f426","<雞>":"1f414","<企鵝>":"1f427","<毛蟲>":"1f41b","<章魚>":"1f419","<魚>":"1f420","<鯨魚>":"1f433","<海豚>":"1f42c","<玫瑰>":"1f339","<花>":"1f33a","<棕櫚樹>":"1f334","<仙人掌>":"1f335","<禮盒>":"1f49d","<南瓜燈>":"1f383","<鬼魂>":"1f47b","<聖誕老人>":"1f385","<聖誕樹>":"1f384","<禮物>":"1f381","<鈴>":"1f514","<慶祝>":"1f389","<氣球>":"1f388","<CD>":"1f4bf","<相機>":"1f4f7","<錄像機>":"1f3a5","<電腦>":"1f4bb","<電視>":"1f4fa","<電話>":"1f4de","<解鎖>":"1f513","<鎖>":"1f512","<鑰匙>":"1f511","<成交>":"1f528","<燈泡>":"1f4a1","<郵箱>":"1f4eb","<浴缸>":"1f6c0","<錢>":"1f4b2","<藥丸>":"1f48a","<橄欖球>":"1f3c8","<籃球>":"1f3c0","<足球>":"26bd","<棒球>":"26be","<高爾夫>":"26f3","<獎盃>":"1f3c6","<入侵者>":"1f47e","<唱歌>":"1f3a4","<吉他>":"1f3b8","<比基尼>":"1f459","<皇冠>":"1f451","<雨傘>":"1f302","<手提包>":"1f45c","<口紅>":"1f484","<戒指>":"1f48d","<鑽石>":"1f48e","<咖啡>":"2615","<啤酒>":"1f37a","<乾杯>":"1f37b","<雞尾酒>":"1f377","<漢堡>":"1f354","<薯條>":"1f35f","<意麪>":"1f35d","<壽司>":"1f363","<麪條>":"1f35c","<煎蛋>":"1f373","<冰激凌>":"1f366","<蛋糕>":"1f382","<蘋果>":"1f34f","<飛機>":"2708","<火箭>":"1f680","<自行車>":"1f6b2","<高鐵>":"1f684","<警告>":"26a0","<旗>":"1f3c1","<男人>":"1f6b9","<女人>":"1f6ba","<O>":"2b55","<X>":"274e","<商標>":"2122"}; 26 var unicodeStr=str.replace(/<.*?>/g,function(a){ 27 if(faceMap[a]){ 28 return toUnicode(faceMap[a]); 29 }else{ 30 return a; 31 } 32 // return a?toUnicode(faceMap[a]):''; 33 }); 34 return htmlEncode(unicodeStr); 35 }
已經基本完成我須要的功能了(兼容至ie6+)。
結語
獻上本人拙劣的demo(css不行)
[emoji picker demo](https://ceau.github.io/emojipicker/demo.html)