├── README.md ├── game.js ├── img ├── balloon.png ├── boom.png └── landscape.png ├── index.html ├── package.json └── style.css /README.md: -------------------------------------------------------------------------------- 1 | # Простая HTML5 игра 2 | 3 | [Демо-версия игры](https://netology-code.github.io/neto-bubble-game/) 4 | 5 | Лопни как можно больше шариков за 15 секунд. Игра создана в рамках [открытого занятия](http://netology.ru/free-lessons/dev-browser-game) по случаю очередного набора курса по [Web API](http://netology.ru/programs/html-javascript) в университете [Нетология](http://netology.ru/). 6 | 7 | Для запуска локально: 8 | 9 | 1. Клонируйте репозиторий себе `git clone https://github.com/netology-code/neto-bubble-game.git` 10 | 2. Установите зависимости `npm install`. 11 | 3. Запустите локальный сервер `npm start`. 12 | -------------------------------------------------------------------------------- /game.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | function rand(from, to) { 3 | return Math.floor((to - from + 1) * Math.random()) + from; 4 | } 5 | 6 | function randomItem(list) { 7 | const next = list[rand(0, list.length - 1)]; 8 | if (randomItem.prev && randomItem.prev === next) { 9 | return randomItem(list); 10 | } 11 | randomItem.prev = next; 12 | return next; 13 | } 14 | 15 | function loadTopScore() { 16 | if (!localStorage) return 0; 17 | const score = localStorage.getItem('topScore'); 18 | return score ? score : 0; 19 | } 20 | 21 | function saveTopScore(score) { 22 | if (!localStorage) return; 23 | localStorage.setItem('topScore', score); 24 | } 25 | 26 | function show(hole) { 27 | hole.classList.remove('boom'); 28 | hole.classList.add('up'); 29 | } 30 | 31 | function hide(hole) { 32 | hole.classList.remove('up'); 33 | } 34 | 35 | function timeToString(time) { 36 | const MSECONDS_IN_SEC = 1000; 37 | const MSECONDS_IN_MIN = 60 * MSECONDS_IN_SEC; 38 | 39 | let min = Math.floor(time / MSECONDS_IN_MIN); 40 | let sec = Math.floor((time % MSECONDS_IN_MIN) / MSECONDS_IN_SEC); 41 | let msec = (time % MSECONDS_IN_MIN) % MSECONDS_IN_SEC; 42 | let spacer = msec > 500 ? ':' : ' '; 43 | return [min, sec] 44 | .map(number => number >= 10 ? number : `0${number}`) 45 | .join(spacer); 46 | } 47 | 48 | function updateTimer() { 49 | if (!isStarted) { 50 | return; 51 | } 52 | 53 | let timeout = GAME_TIMEOUT - (Date.now() - startedAt); 54 | if (timeout < 0 ) { 55 | timeout = 0; 56 | } 57 | timer.innerHTML = timeToString(timeout); 58 | } 59 | 60 | function updateScoreboard(points) { 61 | scoreboard.dataset.points = points; 62 | bestScore.dataset.points = topScore; 63 | } 64 | 65 | function handleClick() { 66 | const hole = this.parentElement; 67 | if (!hole.timeout) { 68 | return; 69 | } 70 | clearTimeout(hole.timeout); 71 | hole.classList.add('boom'); 72 | setTimeout(() => { 73 | hide(hole); 74 | ++points; 75 | updateScoreboard(points); 76 | }, 50); 77 | } 78 | 79 | function next() { 80 | const hole = randomItem(holes); 81 | show(hole); 82 | hole.timeout = setTimeout(i => { 83 | hide(hole); 84 | }, rand(800, 2500)); 85 | } 86 | 87 | function tic() { 88 | setTimeout(() => { 89 | if (isStarted) { 90 | next(); 91 | tic(); 92 | } else { 93 | startButton.style.display = 'initial'; 94 | topScore = Math.max(points, topScore); 95 | saveTopScore(topScore); 96 | updateScoreboard(points); 97 | clearInterval(timerInterval); 98 | } 99 | }, rand(500, 2500)); 100 | } 101 | 102 | function start() { 103 | points = 0; 104 | startedAt = Date.now(); 105 | updateScoreboard(points); 106 | startButton.style.display = 'none'; 107 | isStarted = true; 108 | setTimeout(() => isStarted = false, GAME_TIMEOUT); 109 | timerInterval = setInterval(updateTimer, 250); 110 | tic(); 111 | } 112 | 113 | const GAME_TIMEOUT = 15000; 114 | let timeout, timerInterval, isStarted = false, startedAt; 115 | let topScore = loadTopScore(); 116 | let points = 0; 117 | 118 | const holes = document.getElementsByClassName('hole'); 119 | const bubbles = document.getElementsByClassName('bubble'); 120 | const scoreboard = document.getElementById('currentScoreView'); 121 | const bestScore = document.getElementById('topScoreView'); 122 | const startButton = document.querySelector('.startButton'); 123 | const timer = document.querySelector('.timer'); 124 | Array.from(bubbles).forEach(bubble => bubble.addEventListener('click', handleClick)); 125 | startButton.addEventListener('click', start); 126 | 127 | updateScoreboard(points); 128 | -------------------------------------------------------------------------------- /img/balloon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VitRod/JavaScript/2b077f31cf086617618c7fd665cac62a9cc1a689/img/balloon.png -------------------------------------------------------------------------------- /img/boom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VitRod/JavaScript/2b077f31cf086617618c7fd665cac62a9cc1a689/img/boom.png -------------------------------------------------------------------------------- /img/landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VitRod/JavaScript/2b077f31cf086617618c7fd665cac62a9cc1a689/img/landscape.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |