NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Jira Confetti // @namespace https://stuartcrouch.servegame.com/ // @version 1.0 // @description Make confetti rain after completing planning poker! // @author Stuart Crouch // @license MIT // @match https://j-poker-production.lizardbrain.rocks/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant none // ==/UserScript== (function() { 'use strict'; //-----------Var Inits-------------- var canvas = document.createElement("canvas"); canvas.id = "canvas"; canvas.style.position = "absolute"; canvas.style.backgroundColor = "transparent"; canvas.style.top = 0; canvas.style.left = 0; canvas.style.height = window.innerWidth; canvas.style.width = window.innerHeight; canvas.style.zIndex = 999; var ctx = canvas.getContext("2d"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; var cx = ctx.canvas.width/2; var cy = ctx.canvas.height/2; let confetti = []; const confettiCount = 100; const gravity = 0.5; const terminalVelocity = 5; const drag = 0.075; const colors = [ { front : 'light-blue', back: 'blue'}, { front : 'blue', back: 'darkblue'}, { front : 'DarkSlateBlue', back: 'DeepSkyBlue'}, { front : 'DodgerBlue', back: 'LightSkyBlue'}, ]; //-----------Functions-------------- const resizeCanvas = () => { canvas.width = window.innerWidth; canvas.style.width = window.innerWidth; canvas.height = window.innerHeight; canvas.style.height = window.innerHeight;; cx = ctx.canvas.width/2; cy = ctx.canvas.height/2; } const randomRange = (min, max) => Math.random() * (max - min) + min const initConfetti = () => { resizeCanvas(); document.body.appendChild(canvas); for (let i = 0; i < confettiCount; i++) { confetti.push({ color : colors[Math.floor(randomRange(0, colors.length))], dimensions : { x: randomRange(10, 20), y: randomRange(10, 30), }, position : { x: canvas.width/2, y: canvas.height/2, }, rotation : randomRange(0, 2 * Math.PI), scale : { x: 1, y: 1, }, velocity : { x: randomRange(-25, 25), y: randomRange(-10, -25), }, }); } } //---------Render----------- const render = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); confetti.forEach((confetto, index) => { let width = (confetto.dimensions.x * confetto.scale.x); let height = (confetto.dimensions.y * confetto.scale.y); // Move canvas to position and rotate ctx.translate(confetto.position.x, confetto.position.y); ctx.rotate(confetto.rotation); // Apply forces to velocity confetto.velocity.x -= confetto.velocity.x * drag; confetto.velocity.y = Math.min(confetto.velocity.y + gravity, terminalVelocity); confetto.velocity.x += Math.random() > 0.5 ? Math.random() : -Math.random(); // Set position confetto.position.x += confetto.velocity.x; confetto.position.y += confetto.velocity.y; // Delete confetti when out of frame if (confetto.position.y >= canvas.height) confetti.splice(index, 1); // Loop confetto x position if (confetto.position.x > canvas.width) confetto.position.x = 0; if (confetto.position.x < 0) confetto.position.x = canvas.width; // Spin confetto by scaling y confetto.scale.y = Math.cos(confetto.position.y * 0.1); ctx.fillStyle = confetto.scale.y > 0 ? confetto.color.front : confetto.color.back; // Draw confetto ctx.fillRect(-width / 2, -height / 2, width, height); // Reset transform matrix ctx.setTransform(1, 0, 0, 1, 0, 0); }); if (confetti.length <= 10) { canvas.remove(); } else { window.requestAnimationFrame(render); } } var foundFinishGameHero = false; // subscriber function function domChangeEvaluation(mutations) { // Watch for the hero being removed mutations.forEach((mutation) => { mutation.removedNodes.forEach(function(removedNode) { if ($(removedNode).find('.finished-game-hero').length) { debugger; foundFinishGameHero = false; } }); if (foundFinishGameHero) return; // Watch for the hero being added. mutation.addedNodes.forEach(function(addedNode) { if ($(addedNode).find('.finished-game-hero').length) { var finishedGameNode = $(addedNode).find('.finished-game-hero')[0]; // Start an observer on this node, for when it changes state (becomes visible) const finishedGameObserver = new MutationObserver(launchConfetti); finishedGameObserver.observe( finishedGameNode, { attributes: true } ); foundFinishGameHero = true; } }); }); } // Launch the confetti function launchConfetti(mutations) { initConfetti(); render(); } const pageLoadObserver = new MutationObserver(domChangeEvaluation); pageLoadObserver.observe( document.body, { childList: true, // target childs will be observed | on add/remove subtree: true, // target childs will be observed | on attributes/characterData changes if they observed on target } ); //----------Resize---------- window.addEventListener('resize', function () { resizeCanvas(); }); })();