celebrate with fireworks if bingo was scored
This commit is contained in:
parent
8a05f69cfb
commit
6218869917
235
fireworks.js
Normal file
235
fireworks.js
Normal file
@ -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);
|
||||||
|
};
|
41
index.html
41
index.html
@ -9,11 +9,50 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<script src="script.js" defer></script>
|
<script src="script.js" defer></script>
|
||||||
|
<script src="fireworks.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>7 vs. Wild Bingo <span id="refresh-bingo">🔄</span></h1>
|
<h1>7 vs. Wild Bingo <span id="refresh-bingo">🔄</span></h1>
|
||||||
<table id="bingo"></table>
|
<table id="bingo">
|
||||||
|
<tr>
|
||||||
|
<td data-row="0" data-col="0" class="bingo-field"></td>
|
||||||
|
<td data-row="0" data-col="1" class="bingo-field"></td>
|
||||||
|
<td data-row="0" data-col="2" class="bingo-field"></td>
|
||||||
|
<td data-row="0" data-col="3" class="bingo-field"></td>
|
||||||
|
<td data-row="0" data-col="4" class="bingo-field"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-row="1" data-col="0" class="bingo-field"></td>
|
||||||
|
<td data-row="1" data-col="1" class="bingo-field"></td>
|
||||||
|
<td data-row="1" data-col="2" class="bingo-field"></td>
|
||||||
|
<td data-row="1" data-col="3" class="bingo-field"></td>
|
||||||
|
<td data-row="1" data-col="4" class="bingo-field"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-row="2" data-col="0" class="bingo-field"></td>
|
||||||
|
<td data-row="2" data-col="1" class="bingo-field"></td>
|
||||||
|
<td data-row="2" data-col="2" class="center-field drawn">
|
||||||
|
<img id="center-field-image" alt="Image" src="#">
|
||||||
|
</td>
|
||||||
|
<td data-row="2" data-col="3" class="bingo-field"></td>
|
||||||
|
<td data-row="2" data-col="4" class="bingo-field"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-row="3" data-col="0" class="bingo-field"></td>
|
||||||
|
<td data-row="3" data-col="1" class="bingo-field"></td>
|
||||||
|
<td data-row="3" data-col="2" class="bingo-field"></td>
|
||||||
|
<td data-row="3" data-col="3" class="bingo-field"></td>
|
||||||
|
<td data-row="3" data-col="4" class="bingo-field"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-row="4" data-col="0" class="bingo-field"></td>
|
||||||
|
<td data-row="4" data-col="1" class="bingo-field"></td>
|
||||||
|
<td data-row="4" data-col="2" class="bingo-field"></td>
|
||||||
|
<td data-row="4" data-col="3" class="bingo-field"></td>
|
||||||
|
<td data-row="4" data-col="4" class="bingo-field"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
<p id="seeed">Seeed: <span id="seeed-value"></span> <span id="copy-permalink">📋</span></p>
|
<p id="seeed">Seeed: <span id="seeed-value"></span> <span id="copy-permalink">📋</span></p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
117
script.js
117
script.js
@ -1,3 +1,4 @@
|
|||||||
|
"use strict"
|
||||||
let fields = [
|
let fields = [
|
||||||
// 'MARTINAA/JAAAAA??!!',
|
// 'MARTINAA/JAAAAA??!!',
|
||||||
// '"Du bist doch auch so einer, der ..."',
|
// '"Du bist doch auch so einer, der ..."',
|
||||||
@ -49,19 +50,28 @@ let fields = [
|
|||||||
'kleine Krebse',
|
'kleine Krebse',
|
||||||
|
|
||||||
];
|
];
|
||||||
|
let seed;
|
||||||
const FREE_FIELD_TEXT = "Freies Parken"
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const seed_elem = document.querySelector('#seeed-value');
|
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')) {
|
if (urlParams.has('seeed')) {
|
||||||
seed = urlParams.get('seeed');
|
seed = urlParams.get('seeed');
|
||||||
} else {
|
} else {
|
||||||
seed = Date.now().toString();
|
seed = Date.now().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
const copyPermalinkButton = document.querySelector('#copy-permalink');
|
|
||||||
const copyPermalinkToClipboard = function () {
|
const copyPermalinkToClipboard = function () {
|
||||||
const permalink = window.location.href;
|
const permalink = window.location.href;
|
||||||
navigator.clipboard.writeText(permalink);
|
navigator.clipboard.writeText(permalink);
|
||||||
@ -69,69 +79,75 @@ const copyPermalinkToClipboard = function () {
|
|||||||
copyPermalinkButton.addEventListener('click', copyPermalinkToClipboard);
|
copyPermalinkButton.addEventListener('click', copyPermalinkToClipboard);
|
||||||
|
|
||||||
|
|
||||||
const generateTable = function () {
|
const shuffleBoard = () => {
|
||||||
let prng_hash_seed = cyrb128(seed);
|
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 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()}))
|
.map(value => ({value, sort: rand()}))
|
||||||
.sort((a, b) => a.sort - b.sort)
|
.sort((a, b) => a.sort - b.sort)
|
||||||
.map(({value}) => value)
|
.map(({value}) => value)
|
||||||
|
|
||||||
shuffled.splice(12, 0, FREE_FIELD_TEXT);
|
|
||||||
|
|
||||||
return shuffled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const drawTable = function () {
|
const checkForBingo = (field) => {
|
||||||
let shuffled = generateTable();
|
let bingo = false;
|
||||||
|
let clickedRow = parseInt(field.dataset.row);
|
||||||
|
let clickedCol = parseInt(field.dataset.col);
|
||||||
|
|
||||||
const bingoTable = document.querySelector('#bingo');
|
//check horizontal and vertical axis
|
||||||
let table = document.createElement('table');
|
if (document.querySelectorAll(`#bingo td.bingo-field.drawn[data-row='${clickedRow}']`).length === 5
|
||||||
table.id = 'bingo';
|
|| document.querySelectorAll(`#bingo td.bingo-field.drawn[data-col='${clickedCol}']`).length === 5) {
|
||||||
let tableBody = document.createElement('tbody');
|
bingo = true
|
||||||
let row;
|
}
|
||||||
shuffled.forEach((field, index) => {
|
|
||||||
if (index % 5 === 0) {
|
//check diagonal axis
|
||||||
row = tableBody.insertRow();
|
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();
|
} else if (clickedRow + clickedCol === 4) {
|
||||||
|
if (
|
||||||
let text = document.createTextNode(field);
|
document.querySelector(`#bingo td.bingo-field.drawn[data-row="0"][data-col="4"]`) &&
|
||||||
if (field === FREE_FIELD_TEXT) {
|
document.querySelector(`#bingo td.bingo-field.drawn[data-row="1"][data-col="3"]`) &&
|
||||||
cell.className = 'center-field';
|
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);
|
}
|
||||||
});
|
if (bingo) {
|
||||||
table.appendChild(tableBody);
|
window.fireworks.start();
|
||||||
bingoTable.replaceWith(table);
|
}
|
||||||
|
|
||||||
table.addEventListener('click', (ev) => {
|
|
||||||
let cell;
|
|
||||||
let target_type = ev.target.tagName.toLowerCase();
|
|
||||||
|
|
||||||
if (target_type === "td") {
|
};
|
||||||
cell = ev.target;
|
const drawBingoFieldEventHandler = (ev) => {
|
||||||
} else if (target_type === "img") {
|
let field = ev.target;
|
||||||
cell = ev.target.parentNode;
|
if (field) {
|
||||||
}
|
if (field.classList.contains('drawn')) {
|
||||||
|
field.classList.remove('drawn');
|
||||||
if (cell) {
|
|
||||||
if (cell.classList.contains('drawn')) {
|
|
||||||
cell.classList.remove('drawn');
|
|
||||||
} else {
|
|
||||||
cell.classList.add('drawn');
|
|
||||||
}
|
|
||||||
} else {
|
} 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;
|
seed_elem.innerText = seed;
|
||||||
window.history.pushState(null, null, '?seeed=' + seed);
|
window.history.pushState(null, null, '?seeed=' + seed);
|
||||||
|
|
||||||
const center_field = document.querySelector('.center-field');
|
|
||||||
center_field.innerHTML = '<img src="img/7-vs-wild-logo.svg" alt="7 vs. Wild Logo">'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash function, thx stackoverflow :3
|
// hash function, thx stackoverflow :3
|
||||||
@ -181,9 +197,10 @@ function mulberry32(a) {
|
|||||||
|
|
||||||
drawTable();
|
drawTable();
|
||||||
|
|
||||||
const refreshBingoButton = document.querySelector('#refresh-bingo');
|
|
||||||
const redrawTable = function () {
|
const redrawTable = function () {
|
||||||
|
bingoFields.forEach(elem => elem.classList.remove('drawn'));
|
||||||
seed = Date.now().toString();
|
seed = Date.now().toString();
|
||||||
drawTable()
|
drawTable();
|
||||||
}
|
}
|
||||||
refreshBingoButton.addEventListener('click', redrawTable)
|
refreshBingoButton.addEventListener('click', redrawTable)
|
||||||
|
Loading…
Reference in New Issue
Block a user