Автор оригинала: Maurizio Carboni.
Сегодня, пока я работал, кто-то отправил интересный маленький скрипт в группе чата: Оригинальный скрипт на jsbin Отказ Оригинальный код был написан Мартин Клеппе – Кедо для него для краткосрочной реализации.
setInterval(_=>{ document.body.innerHTML = "" + [ ..."😮😀😁😐😑😬" ][new Date%6] },99)
Код очень прост, но эффект очень интересен. Эмодзи выглядит так, будто это разговаривает.
Теперь мы посмотрим, что делает код. Первая часть кода [... »😮😀😁😐😑😬"]
превращает строку эмоджив в массив. Таким образом, мы можем выбрать единственный элемент из этого массива.
Вы, вероятно, задаетесь вопросом, почему мы преобразуем строку в массив, чтобы выбрать его символ. Это потому, что эмодзи не является единственным характером, но, как правило, четыре байта (эмодзи, на самом деле, выглядит так: «\ xf0 \ x9f \ x98 \ x81»).
С помощью этой техники можно сделать JavaScript Engine сохранить структуру символа Unicode и определить список эмоджин.
[ ..."😮😀😁😐😑😬" ][0] // => 😮 "😮😀😁😐😑😬"[0] // => � (55357) wrong value "😮😀😁😐😑😬".codePointAt(0) // => 128558 correct value String.fromCodePoint("😮😀😁😐😑😬".codePointAt(0)) // => 😮
Как видите, у нас есть два доступа к нужным способам доступа к праву Emoji, а массив один горит короче и проще вспомнить.
Метод массива работает благодаря Итератор Реализация строки объекта Отказ Вместо этого, итерации через каждый байт, как «[N]
, это итерации между каждой кодовой точкой (символ Unicode является кодовой точкой).
Теперь, когда строка – это массив хорошо разделенных эмодзисов, можно выбрать их один за другим, чтобы получить работу Emoji. Emoji выбран последовательно, используя [Новая дата% 6]
Отказ Этот код возвращает другой последовательный Emoji каждая миллисекунд, % 6
Убедитесь, что мы не выходим из границ.
4%6 == 4 5%6 == 5 6%6 == 0 7%6 == 1
Наконец, Документ. Любитель.Иннерхтмл +
Устанавливает эмодзи как единственное содержание страницы.
Но, как видите, эмодзи не меняется так быстро и не меняет каждую миллисекунду, но все еще последовательно.
Это потому что
setInterval(_=>{ },99)
Выполняет функцию каждые 99, и сделав 99 миллисекундров каждый раз, мы делаем % 6
Каждый раз, когда число уменьшилось на один.
x = 0 x += 99 // === 99 x %= 10 // === 9 x += 99 // === 108 x %= 10 // === 8 x += 99 // === 107 x %= 10 // === 7
Теперь, когда мы понимаем, как этот код работает, мы можем начать продлевать его. Функция, о которой я думаю, это сделать текст ниже emoji: http://jsbin.com/sohirid/1/edit?js .utput Отказ
const message = "Hello!"; const delay = 200; setInterval(_=>{ document.body.innerHTML = "" + [ ..."😮😀😁😐😑😬" ][Math.floor(new Date / delay)%6] + "
" + message.substr(0, Math.floor(new Date / delay)%(message.length+1)) },delay)
Предыдущий код собирался обратный. Мне нужно было избежать этого, поэтому мне нужно иметь число, которое увеличивается. Чтобы получить это, я использую Math.floor (Новая дата/задержка)
Отказ Вместо того, чтобы вернуть текущие миллисекунды, он возвращает текущую десятую секунду.
setInterval(_=>{ console.log(Math.floor(new Date / delay)) },delay) // 15133766833 // 15133766834 // 15133766835 // 15133766836 // ...
Теперь, когда число прогрессирует, мне просто нужно ограничить его до диапазона. Для emojis мне нужно придерживаться количества emojis (6), а для текста мне нужна длина текста + 1 (я использую +1, чтобы показать последний символ, запомнить длину% legth).
Результат довольно хороший: эмодзи перемещает рот, и текст отображается ниже. Но движение рта полностью не связано. Я наблюдал, как я родился, так как я родился, и те, которые плохо кукуровали, всегда раздражали меня, поэтому я должен сделать что-то, чтобы сделать эту эмоджи лучше.
Первое, что я искал, было изображение, которое иллюстрирует различные движения рта для каждой буквы. После гугула в течение пяти минут я наконец нашел правильное изображение:
Время для сопоставления различных эмохидов к каждой букве. Для этого мне нужна хорошая страница со всеми эмориями, которые я могу понадобиться: https://emojipedia.org/apple/ Отказ
Выбрав один для каждого уникального движения рта на картинке, я продолжаю создать карту:
const emojiMap = { "😮": ["o", "e"], "😐": ["b", "p", "m"], "🙂": ["c", "g", "j", "k", "n", "r", "s", "t", "v", "x", "z"], "😲": ["d", "l"], "😯": ["q", "u", "w", "y"], "😀": ["a", "i"] } const defaultEmoji = "😐"
Emoji по умолчанию для не буквенных символов (“”, “!”, “,” …) – теперь мне просто нужно изменить код, чтобы получить правильный emoji.
setInterval(_=>{ const character = messagemessage.toLowerCase()[Math.floor(new Date / delay)%(message.length+1)] document.body.innerHTML = "" + (Object.keys(emojiMap).find(emoji => emojiMap[emoji].includes(character)) || defaultEmoji) + "
" + message.substr(0, Math.floor(new Date / delay)%(message.length+1)) },delay)
Этот новый код подразделяется на две части:
- Нахождение персонажа emoji в настоящее время произносится:
const character = messagemessage.toLowerCase()[Math.floor(new Date / delay)%(message.length+1)]
Код прост, потому что мы используем ту же логику кода, которые показывают сообщение, но выбирая только один символ. Основное отличие – message.tolowerCass ()
Потому что мне нужно, чтобы он был нечувствительным к случаю, когда я проверяю, если персонаж совпадает с теми на моей карте Emoji.
- Выбор правильного эмоджи:
(Object.keys(emojiMap).find(emoji => emojiMap[emoji].includes(character)) || defaultEmoji)
Этот код сначала преобразует карту Emoji в массив Emoji. Таким образом, я могу использовать emojis в качестве клавиш и проверять каждое значение один за другим. Например:
const character = "n" ["o", "e"].includes("n") // false ["b", "p", "m"].includes("n") // false ["c", "g", "j", "k", "n", "r", "s", "t", "v", "x", "z"].includes("n") // true ["d", "l"].includes("n") // false ["q", "u", "w", "y"].includes("n") // false ["a", "i"].includes("n") // false
Функция находки проверит значения один за другим, пока функция, которую я не выполнил для этого значения, возвращает true. В нашем случае функция, которую я использую emoji => emojimap [emoji] .includes (характер)
Отказ Это просто проверяет, включает ли набор символов для этого Emoji, который я ищу.
Если находку ничего не находит, это вернет undefined
это ложное значение. Используя Defaultemoji
Я могу заставить мой код возвращаться
Defaultemoji
Функциональность сейчас работает, эмоджи правильно говорит, но по телефону выглядит ужасно (на Android). Я хочу, чтобы это работать даже на мобильном телефоне, поэтому мне нужно сделать код возвращать Emoji, который равен на каждой платформе. Для этого я собираюсь использовать Twemoji Отказ
Twemoji – библиотека из Twitter, чтобы использовать их эмохис везде. Библиотека проста в использовании, имеет метод с именем Parse, который анализирует строку текста и возвращает строку HTML, где каждый emoji – это изображение.
Это идеально подходит и очень просто для реализации. Сначала я включаю скрипт на странице, и я делаю небольшое изменение для преобразования моих эмоджив на изображения:
document.body.innerHTML = "" + twemoji.parse(Object.keys(emojiMap).find(emoji => emojiMap[emoji].includes(character)) || defaultEmoji)
Изображение немного низкое качество. Может быть, мы можем что-то сделать. Глядя на документацию, я замечаю, что могу использовать SVGS.
Давайте использовать их:
document.body.innerHTML = "" + twemoji.parse(Object.keys(emojiMap).find(emoji => emojiMap[emoji].includes(character)) || defaultEmoji, { folder: 'svg', ext: '.svg' })
Эмадзи теперь выглядит хорошо на мобильном телефоне, но слишком много текста. Чтобы решить, я разделил текст, я показываю словами, и я показываю только последние два:
Трюк прост. Разделить слова, я использую .split ('')
, который нарушает струну в массив струн, разделенных пространством.
const words = message.substr(0, Math.floor(new Date / delay)%(message.length+1)).split(' ')
Затем я получаю текущее слово Emoji говорит, что использует .pop ()
const current = words.pop()
Я добавляю все на страницу, выскакивая слово до последнего.
document.body.innerHTML = "" + ... + "
" + words.pop() + "
" + current
Наконец, осталось единственное незначительное, это то, что на изображении с положением рта не только сингулярных персонажей, но и комбинаций, таких как Sh
и TH
Отказ
Было бы удивительно, если скрипт мог поймать их.
Чтобы поймать эти два комбинации персонажей, мне нужно посмотреть не только текущий символ, но и предыдущий и следующий.
const character = message.toLowerCase()[index] const previousDouble = index > 0 ? message.toLowerCase().substr(index - 1, index + 1) : "" const nextDouble = message.toLowerCase().substr(index, index + 2)
Код, который я только что написал создает три переменных: текущий символ, текущий символ вместе с предыдущим и текущим символом вместе с следующим.
const message = "arnold" const index = 1 // character -> "r" // previousDouble -> "ar" // nextDouble -> "rn" const index = 2 // character -> "n" // previousDouble -> "rn" // nextDouble -> "no"
Теперь, когда у нас есть все эти три переменных, нам нужно дать приоритет пару персонажей. Для этого мы впервые ищем предыдущийДубил
на карте эмоджи. Если мы ничего не найдем, мы ищем NextDouble
И, наконец, для текущего персонаж
Отказ
emojis.find(e => emojiMap[e].indexOf(previousDouble) !== -1) || emojis.find(e => emojiMap[e].indexOf(nextDouble) !== -1) || emojis.find(e => emojiMap[e].indexOf(character) !== -1)
Наконец, немного косметического улучшения и говорящий эмодзи делается!
Есть еще много места для улучшения, вот пара идей, если вы хотите расширить этот скрипт дальше:
- Используйте Веб-речь API сделать эмодзи разговаривать на самом деле. У этого будет несколько вызов, например, синхронизации «движения губ» с голосом. Сложность: 4/5
- Использовать D3 Анимировать переход от одного эмодзи до другого, что делает его более естественным и реалистичным. Сложность: 3/5
- Хватит с помощью Emoji и используйте более выразительные изображения, а также распознавать больше дифтонгов. Это легче, потому что код потребности почти 0 изменений. Сложность: 1/5.