Блог от AS3Coder'a о JavaScript, HTML, CSS... и немного о Flash.

пятница, 29 августа 2014 г.

Emoji на вашем сайте

Что такое Emoji?

Emoji - это способ передачи электронных сообщений, состоящих не из букв, а из значков и смайликов. Придумали такой способ общения в Японии и вскоре он получил популярность по всему миру. С подробной историей появления и развития вы можете ознакомиться здесь.

Как это выглядит?

Вот так:



Изображения вставляются прямо в текст. Каждая иконка имеет свой юникод, такой же как и обычный текстовый символ. Все иконки делятся на несколько семантических групп:
  1. Эмоции
  2. Декорации
  3. Транспорт и навигация
  4. Закрытые (внутри геометрической фигуры)
  5. Несортированные
  6. Дополнительные
У каждой группы свой диапазон юникодов. Более подробно можно ознакомиться по ссылке.

В чем заключается трудность

Дело в том, что на сайтах обычно используется кодировка 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

Теперь, имея файл с изображением всех иконок и координаты к ним, приступим к написанию кода.

Алгоритм будет следующий:
  1. Ищем в тексте символы с юникодом в известных нам диапазонах.
  2. Заменяем найденные символы на текстовые элементы 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 кб)

Смотрите также:

2 комментария:

  1. Добрый день!
    Я сколько не читал про эти эмоджи и все равно не понял, как они добавляются в текст. Пока не пришел к одному выводу: они что, представляют собой стандартный набор файлов шрифтов, в которых вместо букв рисунки (к которым дается определенный код) и отображаются у всех (заранее поддерживаемых эмоджи) браузерах?
    Я считал, что это обычные смайлики, добавляемые путем ввода bb-кодов, которые хранятся на других серверах.
    🗯

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например <b>, <i>, <a>

Поиск по блогу

Обо мне



Farid Shamsutdinov (AS3Coder)
Russia, Tatarstan, Kazan
as3coder@gmail.com

Подробнее...

Постоянные читатели

© 2014 Farid Shamsutdinov. При копировании материалов, ссылка на источник обязательна. Технологии Blogger.