From 6218869917ea4da7f95816b6fbdb4ec31cbe327b Mon Sep 17 00:00:00 2001 From: and94x Date: Sat, 3 Dec 2022 13:29:48 +0100 Subject: [PATCH] celebrate with fireworks if bingo was scored --- fireworks.js | 235 +++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 41 ++++++++- script.js | 117 ++++++++++++++----------- style.css | 2 +- 4 files changed, 343 insertions(+), 52 deletions(-) create mode 100644 fireworks.js diff --git a/fireworks.js b/fireworks.js new file mode 100644 index 0000000..dc3790d --- /dev/null +++ b/fireworks.js @@ -0,0 +1,235 @@ +window.requestAnimFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +var canvas = null, + ctx = null, + cw = window.innerWidth, + ch = window.innerHeight, + fireworksLimit = 10, + fireworksShown = 0, + fireworks = [], + particles = [], + hue = 120, + timerTotal = 25, + timerTick = 0, + animationEnd = true; + +function random(min, max) { + return Math.random() * (max - min) + min; +} + +/* To sprawdzic... */ +function calculateDistance(p1x, p1y, p2x, p2y) { + var xDistance = p1x - p2x, + yDistance = p1y - p2y; + return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2)); +} + +function Firework(sx, sy, tx, ty) { + this.x = sx; + this.y = sy; + + this.sx = sx; + this.sy = sy; + + this.tx = tx; + this.ty = ty; + + this.distanceToTarget = calculateDistance(sx, sy, tx, ty); + this.distanceTraveled = 0; + + this.coordinates = []; + this.coordinateCount = 3; + + while (this.coordinateCount--) { + this.coordinates.push([this.x, this.y]); + } + + this.angle = Math.atan2(ty - sy, tx - sx); + this.speed = 2; + this.acceleration = 1.05; + this.brightness = random(50, 70); + this.targetRadius = 1; +} + +Firework.prototype.update = function (index) { + this.coordinates.pop(); + this.coordinates.unshift([this.x, this.y]); + + if (this.targetRadius < 8) { + this.targetRadius += 0.3; + } else { + this.targetRadius = 1; + } + + // speed up the firework + this.speed *= this.acceleration; + + // get the current velocities based on angle and speed + var vx = Math.cos(this.angle) * this.speed, + vy = Math.sin(this.angle) * this.speed; + // how far will the firework have traveled with velocities applied? + this.distanceTraveled = calculateDistance(this.sx, this.sy, this.x + vx, this.y + vy); + + if (this.distanceTraveled >= this.distanceToTarget) { + createParticles(this.tx, this.ty); + // remove the firework, use the index passed into the update function to determine which to remove + fireworks.splice(index, 1); + } else { + // target not reached, keep traveling + this.x += vx; + this.y += vy; + } +} + +// draw firework +Firework.prototype.draw = function () { + ctx.beginPath(); + // move to the last tracked coordinate in the set, then draw a line to the current x and y + ctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]); + ctx.lineTo(this.x, this.y); + ctx.strokeStyle = 'hsl(' + hue + ', 100%, ' + this.brightness + '%)'; + ctx.stroke(); +} + +function Particle(x, y) { + this.x = x; + this.y = y; + // track the past coordinates of each particle to create a trail effect, increase the coordinate count to create more prominent trails + this.coordinates = []; + this.coordinateCount = 5; + while (this.coordinateCount--) { + this.coordinates.push([this.x, this.y]); + } + // set a random angle in all possible directions, in radians + this.angle = random(0, Math.PI * 2); + this.speed = random(1, 10); + // friction will slow the particle down + this.friction = 0.98; + // gravity will be applied and pull the particle down + this.gravity = 1; + // set the hue to a random number +-50 of the overall hue variable + this.hue = random(hue - 50, hue + 50); + this.brightness = random(50, 80); + this.alpha = 1; + // set how fast the particle fades out + this.decay = random(0.015, 0.03); +} + +// update particle +Particle.prototype.update = function (index) { + // remove last item in coordinates array + this.coordinates.pop(); + // add current coordinates to the start of the array + this.coordinates.unshift([this.x, this.y]); + // slow down the particle + this.speed *= this.friction; + // apply velocity + this.x += Math.cos(this.angle) * this.speed; + this.y += Math.sin(this.angle) * this.speed + this.gravity; + // fade out the particle + this.alpha -= this.decay; + + // remove the particle once the alpha is low enough, based on the passed in index + if (this.alpha <= this.decay) { + particles.splice(index, 1); + } + + if (particles.length === 0) { + if (fireworksShown === fireworksLimit) { + animationEnd = true; + } + } +} + +// draw particle +Particle.prototype.draw = function () { + ctx.beginPath(); + // move to the last tracked coordinates in the set, then draw a line to the current x and y + ctx.moveTo(this.coordinates[this.coordinates.length - 1][0], this.coordinates[this.coordinates.length - 1][1]); + ctx.lineTo(this.x, this.y); + ctx.strokeStyle = 'hsla(' + this.hue + ', 100%, ' + this.brightness + '%, ' + this.alpha + ')'; + ctx.stroke(); +} + +// create particle group/explosion +function createParticles(x, y) { + // increase the particle count for a bigger explosion, beware of the canvas performance hit with the increased particles though + var particleCount = 500; + while (particleCount--) { + particles.push(new Particle(x, y)); + } +} + +function loop() { + if (animationEnd === true) { + animationFinish(); + return true; + } + + requestAnimationFrame(loop); + hue = random(0, 360); + + ctx.globalCompositeOperation = 'destination-out'; + ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; + ctx.fillRect(0, 0, cw, ch); + ctx.globalCompositeOperation = 'lighter'; + + var i = fireworks.length; + while (i--) { + fireworks[i].draw(); + fireworks[i].update(i); + } + + var i = particles.length; + while (i--) { + particles[i].draw(); + particles[i].update(i); + } + + if (timerTick >= timerTotal) { + // start the firework at the bottom middle of the screen, then set the random target coordinates, the random y coordinates will be set within the range of the top half of the screen + if (fireworksShown < fireworksLimit) { + fireworks.push(new Firework(cw / 2, ch, random(0, cw), random(0, ch / 2))); + fireworksShown++; + timerTick = 0; + } + } else { + timerTick++; + } +} + +//window.onload = loop; +window.fireworks.start = function () { + /* Prevent multiple animations */ + if (animationEnd === false) { + return false; + } + + fireworksShown = 0; + animationEnd = false; + canvas = document.createElement('canvas'); + document.body.appendChild(canvas); + ctx = canvas.getContext('2d'); + canvas.style.zIndex = 100; + canvas.style.top = 0; + canvas.style.left = 0; + canvas.style.position = 'fixed'; + canvas.width = cw; + canvas.height = ch; + //canvas.style.background = "rgba(0,0,0,0.7)"; + + loop(); + + //setTimeout(function(){animationEnd = true;}, 4500); +} + +animationFinish = function () { + document.body.removeChild(canvas); +}; diff --git a/index.html b/index.html index b577b4b..27cb6ad 100644 --- a/index.html +++ b/index.html @@ -9,11 +9,50 @@ +

7 vs. Wild Bingo 🔄

-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Image +

Seeed: 📋

diff --git a/script.js b/script.js index d2eaa47..bf96e7a 100644 --- a/script.js +++ b/script.js @@ -1,3 +1,4 @@ +"use strict" let fields = [ // 'MARTINAA/JAAAAA??!!', // '"Du bist doch auch so einer, der ..."', @@ -49,19 +50,28 @@ let fields = [ 'kleine Krebse', ]; - -const FREE_FIELD_TEXT = "Freies Parken" +let seed; const urlParams = new URLSearchParams(window.location.search); const seed_elem = document.querySelector('#seeed-value'); +const centerFieldImage = document.querySelector('#center-field-image'); +const bingoFields = document.querySelectorAll('#bingo td.bingo-field'); +const refreshBingoButton = document.querySelector('#refresh-bingo'); +const copyPermalinkButton = document.querySelector('#copy-permalink'); + +centerFieldImage.src = "img/7-vs-wild-logo.svg"; +centerFieldImage.alt = "7 vs. Wild Logo"; + +/** + * Reads the seed from URL Parameter or generates new seed + */ -let seed; if (urlParams.has('seeed')) { seed = urlParams.get('seeed'); } else { seed = Date.now().toString(); } -const copyPermalinkButton = document.querySelector('#copy-permalink'); + const copyPermalinkToClipboard = function () { const permalink = window.location.href; navigator.clipboard.writeText(permalink); @@ -69,69 +79,75 @@ const copyPermalinkToClipboard = function () { copyPermalinkButton.addEventListener('click', copyPermalinkToClipboard); -const generateTable = function () { +const shuffleBoard = () => { let prng_hash_seed = cyrb128(seed); let rand = sfc32(prng_hash_seed[0], prng_hash_seed[1], prng_hash_seed[2], prng_hash_seed[3]); - let shuffled = fields + return fields .map(value => ({value, sort: rand()})) .sort((a, b) => a.sort - b.sort) .map(({value}) => value) +} - shuffled.splice(12, 0, FREE_FIELD_TEXT); +const checkForBingo = (field) => { + let bingo = false; + let clickedRow = parseInt(field.dataset.row); + let clickedCol = parseInt(field.dataset.col); - return shuffled; -} + //check horizontal and vertical axis + if (document.querySelectorAll(`#bingo td.bingo-field.drawn[data-row='${clickedRow}']`).length === 5 + || document.querySelectorAll(`#bingo td.bingo-field.drawn[data-col='${clickedCol}']`).length === 5) { + bingo = true + } -const drawTable = function () { - let shuffled = generateTable(); - - const bingoTable = document.querySelector('#bingo'); - let table = document.createElement('table'); - table.id = 'bingo'; - let tableBody = document.createElement('tbody'); - let row; - shuffled.forEach((field, index) => { - if (index % 5 === 0) { - row = tableBody.insertRow(); + //check diagonal axis + if (clickedRow === clickedCol) { + if ( + document.querySelector(`#bingo td.bingo-field.drawn[data-row="0"][data-col="0"]`) && + document.querySelector(`#bingo td.bingo-field.drawn[data-row="1"][data-col="1"]`) && + document.querySelector(`#bingo td.bingo-field.drawn[data-row="3"][data-col="3"]`) && + document.querySelector(`#bingo td.bingo-field.drawn[data-row="4"][data-col="4"]`) + ) { + bingo = true; } - let cell = row.insertCell(); - - let text = document.createTextNode(field); - if (field === FREE_FIELD_TEXT) { - cell.className = 'center-field'; + } else if (clickedRow + clickedCol === 4) { + if ( + document.querySelector(`#bingo td.bingo-field.drawn[data-row="0"][data-col="4"]`) && + document.querySelector(`#bingo td.bingo-field.drawn[data-row="1"][data-col="3"]`) && + document.querySelector(`#bingo td.bingo-field.drawn[data-row="3"][data-col="1"]`) && + document.querySelector(`#bingo td.bingo-field.drawn[data-row="4"][data-col="0"]`) + ) { + bingo = true; } - cell.appendChild(text); - }); - table.appendChild(tableBody); - bingoTable.replaceWith(table); - - table.addEventListener('click', (ev) => { - let cell; - let target_type = ev.target.tagName.toLowerCase(); + } + if (bingo) { + window.fireworks.start(); + } - if (target_type === "td") { - cell = ev.target; - } else if (target_type === "img") { - cell = ev.target.parentNode; - } - if (cell) { - if (cell.classList.contains('drawn')) { - cell.classList.remove('drawn'); - } else { - cell.classList.add('drawn'); - } +}; +const drawBingoFieldEventHandler = (ev) => { + let field = ev.target; + if (field) { + if (field.classList.contains('drawn')) { + field.classList.remove('drawn'); } else { - console.error('Sum ting wong (*  ̄︿ ̄)'); + field.classList.add('drawn'); + checkForBingo(field); } + } else { + console.error('Sum ting wong (*  ̄︿ ̄)'); + } +}; + +const drawTable = () => { + shuffleBoard().forEach((field, index) => { + bingoFields[index].textContent = field; + bingoFields[index].addEventListener('click', drawBingoFieldEventHandler); }); seed_elem.innerText = seed; window.history.pushState(null, null, '?seeed=' + seed); - - const center_field = document.querySelector('.center-field'); - center_field.innerHTML = '7 vs. Wild Logo' } // hash function, thx stackoverflow :3 @@ -181,9 +197,10 @@ function mulberry32(a) { drawTable(); -const refreshBingoButton = document.querySelector('#refresh-bingo'); + const redrawTable = function () { + bingoFields.forEach(elem => elem.classList.remove('drawn')); seed = Date.now().toString(); - drawTable() + drawTable(); } refreshBingoButton.addEventListener('click', redrawTable) diff --git a/style.css b/style.css index d32e033..70edc8d 100644 --- a/style.css +++ b/style.css @@ -47,7 +47,7 @@ h1, p { margin: 5px; } -p#seeed{ +p#seeed { user-select: text; }