Что такое 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 кб)