Что такое Emoji?
Emoji - это способ передачи электронных сообщений, состоящих не из букв, а из значков и смайликов. Придумали такой способ общения в Японии и вскоре он получил популярность по всему миру. С подробной историей появления и развития вы можете ознакомиться здесь.
Как это выглядит?
Вот так:
 
Изображения вставляются прямо в текст. Каждая иконка имеет свой юникод, такой же как и обычный текстовый символ. Все иконки делятся на несколько семантических групп:
В чем заключается трудность
Дело в том, что на сайтах обычно используется кодировка UTF-8. И отображаться на таком сайте наш пример будет так:
 
А всё потому, что в кодировке UTF-8 нет описания для этих символов. Большинство имеют код из неиспользуемого диапазона начиная с 4 байт.
Поспешу сообщить, что не всё так плохо. И уже сегодня некоторые браузеры решили проблему самостоятельно. Они находят в тексте такие символы и подменяют из на свои изображения.
Вот наш пример в InternetExplorer 11 (Windows 8.1):
 
В FireFox 31:
 
В Mobile Safari (iOS):
 
Но в Chrome мы увидим всё тот же набор квадратиков, который называется шумом:
 
Отсюда выделим следующие проблемы:
Что делать?
А делать мы будем ровным счетов тоже самое, что делают остальные браузеры: искать символы в тексте и подменять их на свои изображения c помощью JavaScript.
Поиск и замена символов на иконки
Подготовка
Чтобы заменять символы на иконки, нам понадобятся их изображения. В интернете можно найти много вариантов, но мы будем использовать стандартный и наиболее популярный набор, используемый в операционной системе iOS.
Я нашел такое изображение в проекте модулей для node.js:
 
Загрузить файл можно по ссылке: https://raw.githubusercontent.com/node-modules/emoji/master/lib/emoji.png
Там же я нашел и описание для этой сетки иконок в CSS-файле:
Теперь, имея файл с изображением всех иконок и координаты к ним, приступим к написанию кода.
Алгоритм будет следующий:
Таким образом, вместо emoji-символов мы получим span-элементы , с заданными размерами и фоновым изображением. Они будут корректно отображаться всегда и во всех браузерах.
Анализ
Проанализировав таблицу символов, я поделил их на несколько групп по диапазону кодов:
  
Суррогатные пары
Что это такое?
Суррогатными парами обычно описываются символы, коды которых не помещаются в формат. Они описываются по определенным правилам формата. Мы не будем разбирать это в рамках данной статьи.
В нашем случае большинство emoji-иконок описываются кодами превышающими формат UTF-8. Поэтому мы использовали суррогатные пары в приведенных выше диапазонах. Для этого использовался такой вот метод:
Теперь попробуем написать код для поиска и замены для каждой из этих групп.
1. Флаги:
    
Исходный код примера (163 кб)
Смотрите также:
Emoji - это способ передачи электронных сообщений, состоящих не из букв, а из значков и смайликов. Придумали такой способ общения в Японии и вскоре он получил популярность по всему миру. С подробной историей появления и развития вы можете ознакомиться здесь.
Как это выглядит?
Вот так:
 
Изображения вставляются прямо в текст. Каждая иконка имеет свой юникод, такой же как и обычный текстовый символ. Все иконки делятся на несколько семантических групп:
- Эмоции
- Декорации
- Транспорт и навигация
- Закрытые (внутри геометрической фигуры)
- Несортированные
- Дополнительные
В чем заключается трудность
Дело в том, что на сайтах обычно используется кодировка UTF-8. И отображаться на таком сайте наш пример будет так:
 
А всё потому, что в кодировке UTF-8 нет описания для этих символов. Большинство имеют код из неиспользуемого диапазона начиная с 4 байт.
Поспешу сообщить, что не всё так плохо. И уже сегодня некоторые браузеры решили проблему самостоятельно. Они находят в тексте такие символы и подменяют из на свои изображения.
Вот наш пример в InternetExplorer 11 (Windows 8.1):
 
В FireFox 31:
 
В Mobile Safari (iOS):
 
Но в Chrome мы увидим всё тот же набор квадратиков, который называется шумом:
 
Отсюда выделим следующие проблемы:
- Отображение шума вместо иконок в некоторых браузерах
- Не одинаковый вид на различных устройствах
Что делать?
А делать мы будем ровным счетов тоже самое, что делают остальные браузеры: искать символы в тексте и подменять их на свои изображения c помощью JavaScript.
Поиск и замена символов на иконки
Подготовка
Чтобы заменять символы на иконки, нам понадобятся их изображения. В интернете можно найти много вариантов, но мы будем использовать стандартный и наиболее популярный набор, используемый в операционной системе iOS.
Я нашел такое изображение в проекте модулей для node.js:
 
Загрузить файл можно по ссылке: https://raw.githubusercontent.com/node-modules/emoji/master/lib/emoji.png
Там же я нашел и описание для этой сетки иконок в CSS-файле:
...
.emoji { background: url("emoji.png") top left no-repeat; width:20px; height: 20px; }
.emoji2600  { background-position: -500px -120px; }
.emoji2601  { background-position: -500px -140px; }
.emoji2614  { background-position: -500px -200px; }
.emoji26c4  { background-position: -520px -200px; }
.emoji26a1  { background-position: -520px -100px; }
.emoji1f300 { background-position: -20px -500px; }
.emoji1f301 { background-position: -20px -520px; }
.emoji1f302 { background-position: -20px -540px; }
.emoji1f303 { background-position: -20px -560px; }
.emoji1f304 { background-position: -20px -580px; }
...
Файл доступен по адресу: https://raw.githubusercontent.com/node-modules/emoji/master/lib/emoji.cssТеперь, имея файл с изображением всех иконок и координаты к ним, приступим к написанию кода.
Алгоритм будет следующий:
- Ищем в тексте символы с юникодом в известных нам диапазонах.
- Заменяем найденные символы на текстовые элементы span, для которых указываем CSS-класс с HEX-кодом.
Таким образом, вместо emoji-символов мы получим span-элементы , с заданными размерами и фоновым изображением. Они будут корректно отображаться всегда и во всех браузерах.
Анализ
Проанализировав таблицу символов, я поделил их на несколько групп по диапазону кодов:
| Группа | Размер символа | Коды | |
|---|---|---|---|
| 1. | Флаги | 8 байт | Представлены в виде 2-х суррогатных пар символов в диапазоне: \ud83c\udde8-ud83c\uddfa (1-я пара) + \ud83c\udde7-\ud83c\uddfa (2-я пара) | 
| 2. | Числа | 4 байта | Представлены в виде двух 2 байтных символов идущих подряд с диапазоном \u0023-\u0039 (1-й символ) и \u20E3 (2 символ) | 
| 3. | Несортированные | 3 байта | Символы с кодами от \u2139 до \u3299 | 
| 4. | Пунктуация | 3 байта | Два символа с кодами \u203C и \u2049 | 
| 5. | Все остальные | 4 байта | Представлены в виде суррогатной пары в диапазоне \ud800-\udbff (1-я часть пары) + \udc00-\udfff (2-я часть пары) | 
Суррогатные пары
Что это такое?
Суррогатными парами обычно описываются символы, коды которых не помещаются в формат. Они описываются по определенным правилам формата. Мы не будем разбирать это в рамках данной статьи.
В нашем случае большинство emoji-иконок описываются кодами превышающими формат UTF-8. Поэтому мы использовали суррогатные пары в приведенных выше диапазонах. Для этого использовался такой вот метод:
function GetHexFromSurrogatePair (a, b)
  {
   return((a - 0xD800) * 0x400 + (b - 0xDC00) + 0x10000);
  }
Но, так как мы будем искать по юникоду, нам нужен обратный метод:function GetSurrogatePairFromHex (hex)
  {
   var a = Math.floor((hex - 0x10000) / 0x400) + 0xD800,
   b = (hex - 0x10000) % 0x400 + 0xDC00;
   return(['\\u' + a.toString(16), '\\u' + b.toString(16)]);
  }
КодТеперь попробуем написать код для поиска и замены для каждой из этих групп.
1. Флаги:
string = string.replace(/(\ud83c[\udde8-\uddfa])(\ud83c[\udde7-\uddfa])/g, function (match)
  {
   var hex = 
     [
      GetHexFromSurrogatePair(match.charCodeAt(0), match.charCodeAt(1)).toString(16),
      GetHexFromSurrogatePair(match.charCodeAt(2), match.charCodeAt(3)).toString(16)
     ].join('');
   //---
   return(['<span class="emoji emoji', hex, '"></span>'].join(''));
  });
2. Числа:string = string.replace(/(\ud83c[\udde8-\uddfa])(\ud83c[\udde7-\uddfa])/g, function (match)
  {
   var hex = 
     [
      GetHexFromSurrogatePair(match.charCodeAt(0), match.charCodeAt(1)).toString(16),
      GetHexFromSurrogatePair(match.charCodeAt(2), match.charCodeAt(3)).toString(16)
     ].join('');
   //---
   return(['<span class="emoji emoji', hex, '"></span>'].join(''));
  });
3. Несортированые:string = string.replace(/[\u0023-\u0039]\u20E3/g, function (match)
  {
   var hex = 
     [
      match.charCodeAt(0).toString(16),
      match.charCodeAt(1).toString(16)
     ].join('');
   //---
   return(['<span class="emoji emoji', hex, '"></span>'].join(''));
  });
4. Пунктуация:string = string.replace(/[\u2139-\u3299]/g, function (match)
  {
   var hex = match.charCodeAt(0).toString(16);
   //---
   return(['<span class="emoji emoji', hex, '"></span>'].join(''));
  });
5. Все остальные:string = string.replace(/[\u203C\u2049]/g, function (match)
  {
   var hex = match.charCodeAt(0).toString(16);
   //---
   return(['<span class="emoji emoji', hex, '"></span>'].join(''));
  });
Теперь осталось собрать и причесать весь код в один итоговый класс, который будет выполнять замену всех групп по очереди:/**
 * Emoji class
 * @description http://as3coder.blogspot.com/2014/08/emoji.html
 * @author AS3Coder
 */
(function(){
/**
 * Define public access
 * @private
 */
var emoji = window.emoji = {};
emoji.replace = Replace;
/**
 * Grouping by range
 * @constant
 * @private
 */
var GROUPS =
  [
   [/(\ud83c[\udde8-\uddfa])(\ud83c[\udde7-\uddfa])/g, ReplaceFlags],     // Flags
   [/[\u0023-\u0039]\u20E3/g,                          ReplaceNumbers],   // Numbers
   [/[\u2139-\u3299]/g,                                ReplaceStandard],  // Unsorted
   [/[\u203C\u2049]/g,                                 ReplaceStandard],  // Punctuation
   [/([\ud800-\udbff])([\udc00-\udfff])/g,             ReplaceSurrogate]  // Other (surrogate pairs)
  ];
/**
 * Method to replace all emoji characters in the icon
 * @param {String} Source string
 * @return {String}
 * @public
 */
function Replace (source)
  {
   var pattern;
   //---
   for(var i=0, j=GROUPS.length; i<j; i++)
     {
      pattern = GROUPS[i];
      if(pattern && pattern[0] && pattern[1])
        {
         if(source.match(pattern[0]))
           {
            source = source.replace(pattern[0], pattern[1]);
           }
        }
     }
   //---
   return(source);
  }
/**
 * Method to replace flags
 * @return {String}
 * @private
 */
function ReplaceFlags (match)
  {
   return(GetHtmlCodeFromHex(
     [
      GetHexFromSurrogatePair(match.charCodeAt(0), match.charCodeAt(1)).toString(16),
      GetHexFromSurrogatePair(match.charCodeAt(2), match.charCodeAt(3)).toString(16)
     ].join('')));
  }
/**
 * Method to replace numbers
 * @return {String}
 * @private
 */
function ReplaceNumbers (match)
  {
   return(GetHtmlCodeFromHex(match.charCodeAt(0).toString(16) + match.charCodeAt(1).toString(16)));
  }
/**
 * Method to replace srandard charters
 * @return {String}
 * @private
 */
function ReplaceStandard (match)
  {
   return(GetHtmlCodeFromHex(match.charCodeAt(0).toString(16)));
  }
/**
 * Method to replace surrogate pairs
 * @return {String}
 * @private
 */
function ReplaceSurrogate (match, p1, p2)
  {
   return(GetHtmlCodeFromHex(GetHexFromSurrogatePair(p1.charCodeAt(0),p2.charCodeAt(0)).toString(16)));
  }
/**
 * The method returns the hex code for a surrogate pair
 * @return {String}
 * @private
 */
function GetHexFromSurrogatePair (a, b)
  {
   return((a - 0xD800) * 0x400 + (b - 0xDC00) + 0x10000);
  }
/**
 * The method returns an html code for icon image
 * @param {String} hex
 * @return {String}
 * @private
 */ 
function GetHtmlCodeFromHex (hex)
  {
   return(['<span class="emojic"><span class="emoji emoji', hex, '"></span><span class="emojit">&#x', hex, ';</span></span>'].join(''));
  }
//---
})();
Пример. Нужно смотреть в разных браузерах| Original text: | After replacing: | 
|---|---|
Исходный код примера (163 кб)
 
 
 
 Сообщения
Сообщения
 
 
Добрый день!
ОтветитьУдалитьЯ сколько не читал про эти эмоджи и все равно не понял, как они добавляются в текст. Пока не пришел к одному выводу: они что, представляют собой стандартный набор файлов шрифтов, в которых вместо букв рисунки (к которым дается определенный код) и отображаются у всех (заранее поддерживаемых эмоджи) браузерах?
Я считал, что это обычные смайлики, добавляемые путем ввода bb-кодов, которые хранятся на других серверах.
🗯
🍒 Отлично
ОтветитьУдалить