Confetti Changer

Hijack submitty's confetti with our own colors (and more features soon™)

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Confetti Changer
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Hijack submitty's confetti with our own colors (and more features soon™)
// @author       Colin SF
// @match        https://submit.scss.tcd.ie/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tcd.ie
// @grant        none
// @license      MIT
// ==/UserScript==

/* eslint-disable no-multi-spaces */ // so the linter doesn't complain about my formatting :)

// to test or preview these hex colors you can use https://www.w3schools.com/colors/colors_hexadecimal.asp or https://www.color-hex.com/
// color themes can be added just by adding a new const list on a new line

const JANUARY_COLORS   = ['#406BC9','#FFFFFF','#809BCE','#9AC8DE','#B6C7BE'];
const FEBRUARY_COLORS  = ['#DF3B57','#EE4B6A','#7D2335','#86CEC5','#B2E6F1'];
const MARCH_COLORS     = ['#8DB62F','#7B9233','#034121','#022607','#FFCC00'];
const APRIL_COLORS     = ['#EED149','#3BCA8B','#9EE0E7','#EBB8AA','#FFFFFF'];
const MAY_COLORS       = ['#F9EAE5','#F16878','#C1DBB3','#7EBC89','#FF8154'];
const JUNE_COLORS      = ['#EC4067','#F4D35E','#F78764','#00889F','#083D77'];
const JULY_COLORS      = ['#FFFFFF','#DE1A1A','#090C9B'];
const AUGUST_COLORS    = ['#F0A202','#FF4040','#F2C940','#AB2321'];
const SEPTEMBER_COLORS = ['#8FD7FF','#316498','#34CA34','#FFFF40','#FF2929','#9C84A4'];
const OCTOBER_COLORS   = ['#000000','#FF6700','#291528'];
const NOVEMBER_COLORS  = ['#5A351E','#522B47','#912F09','#F0A202','#FBF5F3'];
const DECEMBER_COLORS  = ['#D7CDCC','#F7B11D','#1F5E00','#DE1A1A','#FFFFFF'];
const DEFAULT_COLORS   = ['#8FD7FF','#316498','#34CA34','#FFFF40','#FF2929','#9C84A4'];
const MARTINI          = ['#BB0059','#FF1493','#00BFFF','#FFF0F5','#FF69B4','#ADD8E6'];          // sweet girl
const USER_COLORS      = ['#FFFFFF','#808080','#000000'];                                        // YOUR COLORS HERE!

// CONFETTI PARAMETERS
var CONFETTI_COLORS = MARTINI;       // Set color theme here, can change to any const above or "PER_MONTH" if you want submitty's naturally updating confetti
const CONFETTI_DENSITY = 0.75;       // must be a float between [0,inf), 1.0 is submitty's original behavior (but large values will probably tank performance)

// handle per-month confetti color choice
if ( CONFETTI_COLORS == "PER_MONTH" ) {
    let month = new Date().getMonth();
    CONFETTI_COLORS = [
        JANUARY_COLORS,
        FEBRUARY_COLORS,
        MARCH_COLORS,
        APRIL_COLORS,
        MAY_COLORS,
        JUNE_COLORS,
        JULY_COLORS,
        AUGUST_COLORS,
        SEPTEMBER_COLORS,
        OCTOBER_COLORS,
        NOVEMBER_COLORS,
        DECEMBER_COLORS
    ][month];
}

// copy the page's addConfetti() func, alter it so it always uses whatever colors we want
// and then substitute it for the window's original function to customize our confetti
// ( NB: you should never have to go in here unless you're curious :) )
function addConfetti() {
    const canvas = document.getElementById('confetti_canvas');
    if (!canvas) {
        return;
    }
    let times_ran = 0;
    let frame = 0;

    //destroy the canvas animation on click or on enter
    canvas.addEventListener('click', () => {
        if (canvas.style.display !== 'none') {
            canvas.style.display = 'none';
            return;
        }
    });

    window.addEventListener('keypress', (e) => {
        if (e.code === 'Enter' && canvas.style.display !== 'none') {
            canvas.style.display = 'none';
            return;
        }
        if (e.key === 'p' || e.key === 'P'){
            confettiPaused = !confettiPaused;
        }
    });

    // Resume confetti animation when window is visible again
    window.addEventListener('visibilitychange', () => {
        if (document.visibilityState === 'visible' && is_drawing) {
            lastUpdateTime = Date.now();
            draw();
        }
    });

    canvas.width = window.innerWidth;
    const body = document.body;
    const html = document.documentElement;
    canvas.height = Math.max( body.scrollHeight, body.offsetHeight,
                             html.clientHeight, html.scrollHeight, html.offsetHeight );

    canvas.style.display = 'block';

    const ctx = canvas.getContext('2d');
    const pieces = [];
    const numberOfPieces = 2500 * CONFETTI_DENSITY;
    let lastUpdateTime = Date.now();
    const x_const = 0.25;
    const max_times = 250;
    const size_const = 10;
    const gravity_const = 0.25;

    let is_drawing = false;

    function randomColor () {
        return CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)];
    }

    function update () {
        const now = Date.now(),
              dt = now - lastUpdateTime;

        for (let i = pieces.length - 1; i >= 0; i--) {
            const p = pieces[i];

            if (p.y > canvas.height) {
                pieces.splice(i, 1);
                continue;
            }

            p.y += p.gravity * dt;
            p.rotation += p.rotationSpeed * dt;
            p.x += p.x_vel;
        }

        while (pieces.length < numberOfPieces && times_ran < max_times) {
            pieces.push(new Piece(Math.random() * canvas.width, -20));
        }

        lastUpdateTime = now;

        times_ran ++;

        if (times_ran >= max_times * 10) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            canvas.style.display = '';
            canvas.width = 0;
            canvas.height = 0;
        }

    }

    function draw () {

        if (canvas.style.display === 'none') {
            cancelAnimationFrame(frame);
            is_drawing = false;
            return;
        }

        is_drawing = true;

        // Stop the confetti animation if the page is not visible
        if (document.visibilityState === 'hidden') {
            cancelAnimationFrame(frame);
            return;
        }

        update();
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        pieces.forEach((p) => {
            ctx.save();

            ctx.fillStyle = p.color;

            ctx.translate(p.x + p.size / 25, p.y + p.size / 2);
            ctx.rotate(p.rotation);

            ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size);

            ctx.restore();
        });

        frame = requestAnimationFrame(draw);
    }

    function Piece (x, y) {
        this.x = x;
        this.y = y;
        this.x_vel = (Math.random() - 0.5) * x_const;
        this.size = (Math.random() * 0.5 + 0.75) * size_const;
        this.gravity = (Math.random() * 0.5 + 0.75) * gravity_const;
        this.rotation = (Math.PI * 2) * Math.random();
        this.rotationSpeed = (Math.PI * 2) * (Math.random() - 0.5) * 0.0015;
        this.color = randomColor();
    }

    while (pieces.length < numberOfPieces) {
        pieces.push(new Piece(Math.random() * canvas.width, Math.random() * canvas.height));
    }

    draw();
}

(function() {
    'use strict';
    window.addConfetti = addConfetti;
})();