Приветствую, в этой статье мы сделаем игру «Слоты» внутри названия сайта(пример, TitleRun). Механика игры будет очень простая. нажимаешь на кнопку — получаешь случайные слоты, если все слоты совпадают — выигрываешь.

Да, возможно это и звучит как нечто совсем странное, но, всё же, это довольно интересно. Во всяком случае, интереснее, чем делать игру обычным способом.
Анимации можно реализовать разными способами:
Как слоты я буду использовать эмодзи(смайлики, emoji, как вам удобнее):

Вот так я смогу получать случайные слоты:
При нажатии на кнопку, будет анимация разных слотов, скорость которой будет постепенно увеличиваться:
checkWin — функция для проверки выигрыша:
Теперь это уже играбельно.
Но я хотел бы добавить больше анимаций(например, анимации выигрыша и главного меню)
Это можно сделать, если ввести глобальные переменные, типа:
и на основе их показывать анимации:
Первое, что я хотел бы сделать — функция, которая заменит логику winAnimation. Она будет выводить сообщение и после запускать другую функцию. В качестве аргумента она будет принимать объект с текстов, кол-вом итераций, скоростью и колбеком.
Ещё я хотел бы сделать менеджер анимаций, с помощью которого можно будет их запускать и контролировать. Это будет класс с хранилищем анимаций и несколькими методами.
Описание методов:
setAnimation — вызывает анимацию.
isPlay — функция-условие для проверки «играет» ли анимация.
createAnimation — добавляет анимацию в хранилище.
Теперь код будет выглядеть вот так:
Для разнообразия добавим возможность добавить случайный слот.
(Тут мы просто берём emoji из API, добавляем в массив и включаем главную анимацию)
Вот и всё. Надеюсь вы узнали что-нибудь новое.
Репозиторий на GitHub — ссылка.
Сайт-пример — ссылка.

Зачем вообще делать игры в названии сайта?
Да, возможно это и звучит как нечто совсем странное, но, всё же, это довольно интересно. Во всяком случае, интереснее, чем делать игру обычным способом.
Анимации
Анимации можно реализовать разными способами:
setInterval
function animation() { let i = 0; setInterval(() => { document.title = `Таймер - ${i}`; i++; }); }
setTimeout
let timer = 0; function animation() { document.title = `Таймер - ${timer}`; timer++; setTimeout(animation, 1000) }
while и await
Этот способ был в
исходниках TitleRun`a
исходниках TitleRun`a
function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); } async function animation() { let i = 0; while(true) { document.title = `Таймер - ${i}`; await sleep(1000); } }
Механика
Как слоты я буду использовать эмодзи(смайлики, emoji, как вам удобнее):

Вот так я смогу получать случайные слоты:
const getSlot = () => { const slot = Math.ceil(Math.random()*slots.length-1); return slots[slot]; }; const getSlots = () => { return slots.map(getSlot); };
При нажатии на кнопку, будет анимация разных слотов, скорость которой будет постепенно увеличиваться:
const spin = document.getElementById('spin'); const slotsAnimation = async () => { let speed = 20; while (true) { //получаю слоты const slotsRandom = getSlots(); //вывожу document.title = slotsRandom.join(''); if(speed >= 1000) { checkWin(slotsRandom); break; } speed += 50; await sleep(speed); } }; spin.addEventListener('click', StartGame);
checkWin — функция для проверки выигрыша:
const checkWin = (slots) => { for(let i=0;i<slots.length-1;i++) { //Если слот не совпадёт со следующим if(slots[i] !== slots[i+1]) { alert('GAME OVER'); return; } } alert('WIN!!'); };
Теперь это уже играбельно.
Но я хотел бы добавить больше анимаций(например, анимации выигрыша и главного меню)
Это можно сделать, если ввести глобальные переменные, типа:
let playing = false; let win = false;
и на основе их показывать анимации:
const slotsAnimation = async () => { playing = true let speed = 20; while (playing) { const slotsRandom = getSlots(); document.title = slotsRandom.join(''); if(speed >= 1000) { checkWin(slotsRandom); playing = false } speed += 50; await sleep(speed); } }; const winAnimation = async () => { win = true; while(win && !playing) { document.title = 'WIN!*'; await sleep(1000); document.title = '*WIN!'; await sleep(1000); } }; //Эта анимация будет просто циклично дублировать слоты const mainAnimation = async () => { playing = false let i = 0; while (!playing && !win) { if(i === slots.length - 1) i = 0; else i++; document.title = slots[i].repeat(slots.length); await sleep(1000); } };
Делаем удобнее
Первое, что я хотел бы сделать — функция, которая заменит логику winAnimation. Она будет выводить сообщение и после запускать другую функцию. В качестве аргумента она будет принимать объект с текстов, кол-вом итераций, скоростью и колбеком.
const message = async ({text = [], count = 'infinity', speed = 1000, callback = null}) => { if(!text[0]) throw new Error('need array for text'); let i = 0; let textIndex = 0; while(true) { if(count <= i && count !== 'infinity') { // в конце вызовем колбек, если он есть if(callback) callback(); break; } if(textIndex >= text.length) textIndex = 0; //За каждую итерацию цикла будем показывать 1 сообщение из массива сообщений document.title = text[textIndex]; i++; textIndex++; await sleep(speed) } }; // Использование message({text: ['️WINNER', 'WINNER️'], count: 3, speed: 800, callback: mainAnimation})
Ещё я хотел бы сделать менеджер анимаций, с помощью которого можно будет их запускать и контролировать. Это будет класс с хранилищем анимаций и несколькими методами.
class Animations { animations = []; setAnimation(animation, args = {}) { // Выключаем все анимации for(const item of this.animations) { item.play = false } // Находим анимацию, включаем и запускаем функцию const Animation = this.animations.find(item => item.name === animation); if(!Animation) return; Animation.play = true; Animation.callback(args) }; isPlay(animation) { return this.animations.find(item => item.name === animation).play } createAnimation(name, callback) { this.animations.push({ play: false, name, callback, }) } }
Описание методов:
setAnimation — вызывает анимацию.
isPlay — функция-условие для проверки «играет» ли анимация.
createAnimation — добавляет анимацию в хранилище.
Теперь код будет выглядеть вот так:
const checkWin = (slots) => { for(let i=0;i<slots.length-1;i++) { if(slots[i] !== slots[i+1]) { alert('GAME OVER'); animations.setAnimation('main'); return; } } alert('WIN!!'); animations.setAnimation('message', {text: ['️WINNER', 'WINNER️'], count: 3, speed: 800, callback: () => animations.setAnimation('main')}) }; const slotsAnimation = async () => { let speed = 20; while (animations.isPlay('game')) { const slotsRandom = getSlots(); document.title = slotsRandom.join(''); if(speed >= 1000) { checkWin(slotsRandom); } speed += 50; await sleep(speed); } }; const mainAnimation = async () => { let i = 0; while (animations.isPlay('main')) { if(i === slots.length - 1) i = 0; else i++; document.title = slots[i].repeat(slots.length); await sleep(1000); } }; // Добавление анимаций animations.createAnimation('main', mainAnimation); animations.createAnimation('game', slotsAnimation); animations.createAnimation('message', message); // Запуск animations.setAnimation('main');
Возможность добавить слот
Для разнообразия добавим возможность добавить случайный слот.
(Тут мы просто берём emoji из API, добавляем в массив и включаем главную анимацию)
const AddSlot = async () => { // Показываем сообщение пока ждём ответа от API animations.setAnimation('message', {text: ['Getting slot','Getting slot'], speed: 500}); try { const data = await fetch(`https://emoji-api.com/emojis?access_key=КЛЮЧ`); const body = await data.json(); const emoji = await body[Math.floor(Math.random() * body.length -1)]; slots.push(emoji.character); } catch (e) { // Если не получилось console.log(e); animations.setAnimation('none'); document.title = 'Error, try again'; await sleep(800); } finally { animations.setAnimation('main'); } };
Вот и всё. Надеюсь вы узнали что-нибудь новое.
Репозиторий на GitHub — ссылка.
Сайт-пример — ссылка.
