Недавно мы решили проверить байку лингвистов про перемешанные буквы в слове и написали простой скрипт, который это делает. Теоретически, если первая и последняя буквы на своём месте, то слово должно читаться нормально. В прошлый раз мы выяснили, что так работает не всегда.
Сегодня сделаем следующий шаг в эту бездну — добавим красивую визуализацию перемешанным буквам, чтобы сделать процесс более наглядным. Зачем это читать:
- Попрактиковаться в стилях и оформлении страницы.
- Попробовать необычные псевдоклассы в CSS.
- Поработать с анимацией.
- Испытать на практике самовызывающиеся функции.
Логика проекта
Вот как это будет работать:
- Когда пользователь заходит на страницу, он видит перемешивающиеся буквы, которые складываются в текст.
- Пару секунд спустя появляется приглашение попробовать и ниже — поле ввода.
- Пользователь вводит свой текст и нажимает энтер.
- Алгоритм перемешивает буквы в словах и выдаёт анимированный результат.
- Так происходит каждый раз, когда пользователь вводит свой текст и нажимает энтер.
Модуль с перемешиванием букв мы возьмём уже готовый из прошлого проекта, а сегодня сделаем красивую анимацию.
Готовим страницу
Из основных элементов на странице нам нужно разместить только блок с результатом и поле ввода, всё остальное — внешние стили и скрипты. Ещё подключим дополнительный шрифт, чтобы было красивее, и jQuery, чтобы было проще работать с элементами на странице:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Перемешиваем буквы в словах</title>
<!-- подключаем свои стили и шрифт -->
<link rel="stylesheet" href="styles.css" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300" type="text/css" />
</head>
<body>
<!-- тут будет готовый результат -->
<div id="container">Перемешиваем буквы</div>
<!-- поле ввода текста, который нужно перемешать-->
<input type="text" id="userText" />
<!-- подключаем jQuery и скрипты -->
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="jquery.shuffleLetters.js"></script>
<script src="script.js"></script>
</body>
</html>
Настраиваем внешний вид страницы
Теперь создадим файл styles.css и настроим общий вид страницы. Начнём с глобальных настроек и стиля блока с итоговым результатом:
/* убираем везде отступы */
*{
margin:0;
padding:0;
}
/* общие настройки для страницы */
body{
/* фон и отступ */
color:#fff;
padding:10px;
/* минимальная высота */
min-height:600px;
/* настройки шрифта */
font:14px/1.3 'Segoe UI',Arial, sans-serif;
}
/* стили блока с результатом */
#container{
/* цвет текста */
color: #555;
/* размер шрифта */
font-size: 58px;
/* отступы */
margin: 0 auto;
padding: 200px 0 100px;
/* ширина блока */
width: 650px;
/* включаем относительное позиционирование */
position:relative;
/* минимальная высота */
min-height: 90px;
/* настройки шрифта */
font-family:'Open Sans Condensed',sans-serif;
text-shadow:1px 1px 0 rgba(255,255,255,0.5);
}
Теперь добавим в начало итогового блока стрелку — пусть она показывает начало текста, чтобы он не висел в пустоте. Для этого используем псевдоэлемент :before — он отвечает за контент и настройки стилей до выбранного элемента:
/* добавляем указатель-стрелку к блоку с результатом */
#container:before{
/* стрелка */
content: ">";
/* размер шрифта */
font-size: 50px;
/* сдвигаем левее */
left: -40px;
/* прозрачность */
opacity: 0.25;
/* включаем абсолютное позиционирование */
position: absolute;
/* добавляем тень */
text-shadow: 1px 1px 0 white;
top: 210px;
}
Финальный штрих в оформлении — сделаем красивое поле для ввода пользовательского текста. По стилям будет всё то же самое: выравниваем по центру, убираем лишнее и настраиваем шрифт:
/* поле ввода текста */
#userText{
/* убираем фон и границы поля ввода, оставляем только нижнюю линию */
background:none;
border:none;
border-bottom:1px solid #aaa;
outline: none;
/* цвет текста */
color: #777777;
display: block;
/* настройки шрифта */
font-family: 'Open Sans Condensed',sans-serif;
font-size: 20px;
/* отступы */
margin: 0 auto 0px;
padding: 10px;
/* выравнивание текста */
text-align: center;
/* ширина блока */
width: 300px;
}
Разбираем скрипт анимации перемешивания
Чтобы не городить всё в одну кучу, разобьём скрипты на два файла: в одном будет скрипт с анимацией перемешивания букв, в другом — механика всей страницы.
Для первой части задачи про анимацию букв мы возьмём скрипт Мартина Ангелова jquery.shuffleLetters.js
, немного его упростим и прокомментируем в нём каждый шаг, чтобы было понятно, что там происходит.
Весь этот скрипт — это одна самовызывающаяся функция, которая запускается сразу, как только браузер подключает файл с этим скриптом. Но хитрость в том, что это просто обёртка для внутренней функции shuffleLetters
— автозапуск просто загружает её в память и позволяет к ней обратиться в любой момент. Это нам пригодится в основном скрипте.
Также внутри этой функции есть ещё одна функция с автозапуском — shuffle()
. В ней происходит вся магия анимации: она несколько раз показывает произвольные символы и следит за тем, чтобы анимация была плавной и равномерной. Она срабатывает, как только в родительском скрипте браузер доходит до объявления этой функции.
В остальном логика простая: берём символ, несколько раз вместо него показываем что-то другое, а финальным шагом ставим правильный символ и переходим к следующему. Как только счётчик символов в строке скажет, что мы всё обработали, — анимация останавливается. Читайте комментарии, чтобы лучше вникнуть в процесс:
// весь скрипт — это одна самовызывающаяся функция
(function($){
// название функции
$.fn.shuffleLetters = function(prop){
// параметры
var options = $.extend({
// сколько раз поменяется каждый символ
"step" : 8,
// сколько кадров в секунду нужно отображать при анимации
"fps" : 25,
// заглушка для пустого поля ввода
"text" : "",
// при повторном вызове делаем всё то же самое
"callback" : function(){}
},prop)
// результат работы функции
return this.each(function(){
// отсюда будем брать текст — передаём в функцию ссылку на поле ввода
var el = $(this),
// строка, которую нужно обработать, на старте пустая
str = "";
// если на странице уже есть что перемешивать — делим строку на слова
if(options.text) { str = options.text.split(''); }
// иначе делим на слова то, что ввёл пользователь
else { str = el.text().split(''); }
// переменные для типа символов и их позиции в слове
var types = [],
letters = [];
// перебираем все символы в строке
for(var i=0;i<str.length;i++){
// берём очередной символ
var ch = str[i];
// если это пробел — помечаем как пробел
if(ch == " "){
types[i] = "space";
continue;
}
// если это маленькие буквы
else if(/[а-я]/.test(ch)){ types[i] = "lowerLetter"; }
// если большие
else if(/[А-Я]/.test(ch)){ types[i] = "upperLetter"; }
// всё остальное — символы
else { types[i] = "symbol"; }
// запоминаем позицию текущего символа
letters.push(i);
}
// очищаем поле ввода
el.html("");
// функция, которая выполняется сразу после создания
// на вход она получает номер текущего символа для анимации
(function shuffle(start){
var i,
// количество букв для анимации
len = letters.length,
// получаем копию оригинальной строки, чтобы с ней дальше работать
strCopy = str.slice(0);
// если обработали все символы — выходим из функции
if(start>len){ return; }
// перебираем все символы
for(i=Math.max(start,0); i < len; i++){
// если символ ещё не поменялся нужное количество раз
if( i < start+options.step){
// выбираем случайный и ставим его на это место
strCopy[letters[i]] = randomChar(types[letters[i]]);
}
// в противном случае ставим туда пустой символ — признак того, что мы обработали этот символ
else {strCopy[letters[i]] = "";}
}
// оставляем обработанные символы на своём месте в итоговом блоке
el.text(strCopy.join(""));
// готовим анимацию смены букв
setTimeout(function(){
// увеличиваем счётчик использованных букв на единицу
shuffle(start+1);
// держим букву на экране нужное количество времени
},1000/options.fps);
// уменьшаем количество обработанных в анимации букв
})(-options.step);
});
};
// выбираем, что показывать во время перемешивания
function randomChar(type){
var pool = "";
// если это маленькая буква — берём все маленькие и цифры
if (type == "lowerLetter"){
pool = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя0123456789";
}
// если это большие — большие и цифры
else if (type == "upperLetter"){
pool = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ0123456789";
}
// иначе берём только символы
else if (type == "symbol"){
pool = ",.?/\\(^)![]{}*&^%$#'\"";
}
// делим выборку по символам и выбираем оттуда что-то случайным образом
var arr = pool.split('');
return arr[Math.floor(Math.random()*arr.length)];
}
})(jQuery);
Собираем основной скрипт
Создаём файл script.js и сразу добавляем в него код из прошлого проекта, который просто перемешивает буквы в словах и отдаёт новую строку:
// перемешиваем буквы в словах
function shuffelWord(word) {
// делим слово на отдельные буквы
word_spl = word.split('');
// убираем первую и последнюю буквы
let first = word_spl.shift();
let last = '';
// если слово состоит больше чем из одной буквы — запоминаем последнюю
if (word.length > 1) {last = word_spl.pop()}
// перемешиваем оставшиеся буквы
for (let i = word_spl.length - 1; i > 0; i--) {
// берём случайную букву
let j = Math.floor(Math.random() * (i + 1));
// меняем её местами с текущей
[word_spl[i], word_spl[j]] = [word_spl[j], word_spl[i]];
}
// собираем и возвращаем слово
return first + word_spl.join("") + last;
}
Теперь сделаем основной скрипт с такой логикой:
- Получаем доступ к элементам на странице.
- Сразу запускаем анимацию приветствия — к этому моменту у нас уже загружена функция shuffleLetters.
- Ждём 2 секунды и меняем приветственный текст. Его тоже анимируем.
- Параллельно с этим добавляем обработчик нажатия на энтер в поле ввода — так мы научим страницу реагировать на действия пользователя.
- Как только началась анимация пользовательского текста — очищаем поле ввода, чтобы оно не повлияло на анимацию.
// основная функция
$(function(){
// получаем доступ к элементам на странице
var container = $("#container")
userText = $('#userText');
// выводим первую анимацию перемешанных букв в блоке с результатом
container.shuffleLetters();
// очищаем поле ввода по клику на нём
userText.click(function () {
userText.val("");
// добавляем обработчик нажатия на энтер
}).bind('keypress',function(e){
// если нажали энтер
if(e.keyCode == 13){
// разбиваем строку на слова
arr = userText.val().split(' ');
// тут будет финальный результат
var result = '';
// перебираем все слова в тексте
for (let i = 0; i < arr.length; i++) {
// перемешиваем буквы и добавляем новое слово в итоговый текст
result = result + shuffelWord(arr[i]) + ' ';
}
// выводим анимацию перемешанных букв
container.shuffleLetters({
"text": result
});
// очищаем поле ввода текста
userText.val("");
}
// скрываем поле ввода, пока не закончилась первая анимация
}).hide();
// ждём 2 секунды
setTimeout(function(){
// выводим приветственную надпись
container.shuffleLetters({
"text": "Теперь попробуйте сами"
});
// добавляем подсказку в поле ввода
userText.val("Введите текст и нажмите enter..").fadeIn();
},2000);
});
Собираем всё вместе, обновляем страницу и видим, как всё работает:
Посмотреть анимацию на странице проекта
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Перемешиваем буквы в словах</title>
<!-- подключаем свои стили и шрифт -->
<link rel="stylesheet" href="styles.css" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300" type="text/css" />
</head>
<body>
<!-- тут будет готовый результат -->
<div id="container">Перемешиваем буквы</div>
<!-- поле ввода текста, который нужно перемешать-->
<input type="text" id="userText" />
<!-- подключаем jQuery и скрипты -->
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="jquery.shuffleLetters.js"></script>
<script src="script.js"></script>
</body>
</html>
/* убираем везде отступы */
*{
margin:0;
padding:0;
}
/* общие настройки для страницы */
body{
/* фон и отступ */
color:#fff;
padding:10px;
/* минимальная высота */
min-height:600px;
/* настройки шрифта */
font:14px/1.3 'Segoe UI',Arial, sans-serif;
}
/* стили блока с результатом */
#container{
/* цвет текста */
color: #555;
/* размер шрифта */
font-size: 58px;
/* отступы */
margin: 0 auto;
padding: 200px 0 100px;
/* ширина блока */
width: 650px;
/* включаем относительное позиционирование */
position:relative;
/* минимальная высота */
min-height: 90px;
/* настройки шрифта */
font-family:'Open Sans Condensed',sans-serif;
text-shadow:1px 1px 0 rgba(255,255,255,0.5);
}
/* добавляем указатель-стрелку к блоку с результатом */
#container:before{
/* стрелка */
content: ">";
/* размер шрифта */
font-size: 50px;
/* сдвигаем левее */
left: -40px;
/* прозрачность */
opacity: 0.25;
/* включаем абсолютное позиционирование */
position: absolute;
/* добавляем тень */
text-shadow: 1px 1px 0 white;
top: 210px;
}
/* поле ввода текста */
#userText{
/* убираем фон и границы поля ввода, оставляем только нижнюю линию */
background:none;
border:none;
border-bottom:1px solid #aaa;
outline: none;
/* цвет текста */
color: #777777;
display: block;
/* настройки шрифта */
font-family: 'Open Sans Condensed',sans-serif;
font-size: 20px;
/* отступы */
margin: 0 auto 0px;
padding: 10px;
/* выравнивание текста */
text-align: center;
/* ширина блока */
width: 300px;
}
// перемешиваем буквы в словах
function shuffelWord(word) {
// делим слово на отдельные буквы
word_spl = word.split('');
// убираем первую и последнюю буквы
let first = word_spl.shift();
let last = '';
// если слово состоит больше чем из одной буквы — запоминаем последнюю
if (word.length > 1) {last = word_spl.pop()}
// перемешиваем оставшиеся буквы
for (let i = word_spl.length - 1; i > 0; i--) {
// берём случайную букву
let j = Math.floor(Math.random() * (i + 1));
// меняем её местами с текущей
[word_spl[i], word_spl[j]] = [word_spl[j], word_spl[i]];
}
// собираем и возвращаем слово
return first + word_spl.join("") + last;
}
// основная функция
$(function(){
// получаем доступ к элементам на странице
var container = $("#container")
userText = $('#userText');
// выводим первую анимацию перемешанных букв в блоке с результатом
container.shuffleLetters();
// очищаем поле ввода по клику на нём
userText.click(function () {
userText.val("");
// добавляем обработчик нажатия на энтер
}).bind('keypress',function(e){
// если нажали энтер
if(e.keyCode == 13){
// разбиваем строку на слова
arr = userText.val().split(' ');
// тут будет финальный результат
var result = '';
// перебираем все слова в тексте
for (let i = 0; i < arr.length; i++) {
// перемешиваем буквы и добавляем новое слово в итоговый текст
result = result + shuffelWord(arr[i]) + ' ';
}
// выводим анимацию перемешанных букв
container.shuffleLetters({
"text": result
});
// очищаем поле ввода текста
userText.val("");
}
// скрываем поле ввода, пока не закончилась первая анимация
}).hide();
// ждём 2 секунды
setTimeout(function(){
// выводим приветственную надпись
container.shuffleLetters({
"text": "Теперь попробуйте сами"
});
// добавляем подсказку в поле ввода
userText.val("Введите текст и нажмите enter..").fadeIn();
},2000);
});
// весь скрипт — это одна самовызывающаяся функция
(function($){
// название функции
$.fn.shuffleLetters = function(prop){
// параметры
var options = $.extend({
// сколько раз поменяется каждый символ
"step" : 8,
// сколько кадров в секунду нужно отображать при анимации
"fps" : 25,
// заглушка для пустого поля ввода
"text" : "",
// при повторном вызове делаем всё то же самое
"callback" : function(){}
},prop)
// результат работы функции
return this.each(function(){
// отсюда будем брать текст — передаём в функцию ссылку на поле ввода
var el = $(this),
// строка, которую нужно обработать, на старте пустая
str = "";
// если на странице уже есть что перемешивать — делим строку на слова
if(options.text) { str = options.text.split(''); }
// иначе делим на слова то, что ввёл пользователь
else { str = el.text().split(''); }
// переменные для типа символов и их позиции в слове
var types = [],
letters = [];
// перебираем все символы в строке
for(var i=0;i<str.length;i++){
// берём очередной символ
var ch = str[i];
// если это пробел — помечаем как пробел
if(ch == " "){
types[i] = "space";
continue;
}
// если это маленькие буквы
else if(/[а-я]/.test(ch)){ types[i] = "lowerLetter"; }
// если большие
else if(/[А-Я]/.test(ch)){ types[i] = "upperLetter"; }
// всё остальное — символы
else { types[i] = "symbol"; }
// запоминаем позицию текущего символа
letters.push(i);
}
// очищаем поле ввода
el.html("");
// функция, которая выполняется сразу после создания
// на вход она получает номер текущего символа для анимации
(function shuffle(start){
var i,
// количество букв для анимации
len = letters.length,
// получаем копию оригинальной строки, чтобы с ней дальше работать
strCopy = str.slice(0);
// если обработали все символы — выходим из функции
if(start>len){ return; }
// перебираем все символы
for(i=Math.max(start,0); i < len; i++){
// если символ ещё не поменялся нужное количество раз
if( i < start+options.step){
// выбираем случайный и ставим его на это место
strCopy[letters[i]] = randomChar(types[letters[i]]);
}
// в противном случае ставим туда пустой символ — признак того, что мы обработали этот символ
else {strCopy[letters[i]] = "";}
}
// оставляем обработанные символы на своём месте в итоговом блоке
el.text(strCopy.join(""));
// готовим анимацию смены букв
setTimeout(function(){
// увеличиваем счётчик использованных букв на единицу
shuffle(start+1);
// держим букву на экране нужное количество времени
},1000/options.fps);
// уменьшаем количество обработанных в анимации букв
})(-options.step);
});
};
// выбираем, что показывать во время перемешивания
function randomChar(type){
var pool = "";
// если это маленькая буква — берём все маленькие и цифры
if (type == "lowerLetter"){
pool = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя0123456789";
}
// если это большие — большие и цифры
else if (type == "upperLetter"){
pool = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ0123456789";
}
// иначе берём только символы
else if (type == "symbol"){
pool = ",.?/\\(^)![]{}*&^%$#'\"";
}
// делим выборку по символам и выбираем оттуда что-то случайным образом
var arr = pool.split('');
return arr[Math.floor(Math.random()*arr.length)];
}
})(jQuery);