const canvas = document.querySelector('canvas#bg'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const snowing = true; const ctx = canvas.getContext('2d'); const FILL_STYLES = { light: 'rgba(0,0,0,0.05)', dark: 'rgba(255,255,255,0.15)', snowing: 'rgb(206, 212, 220)' }; const BACKGROUNDS = { evening: 'linear-gradient(#011d32, #3b3b89)', night: '#000000' }; ctx.fillStyle = snowing ? FILL_STYLES.snowing : FILL_STYLES.light; let darkMode = 0; if(snowing) { document.body.classList.add('dark-mode'); document.body.style.background = BACKGROUNDS.evening; } addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; // need to set ctx.fillStyle whenever I resize??? ctx.fillStyle = darkMode ? FILL_STYLES.dark : snowing ? FILL_STYLES.snowing : FILL_STYLES.light; }); function Point() { const r = snowing ? 4 : 8; // progress below 0 is neglected, negative initial // progress serves to introduce random delays - // at 0.05 progress points per second, for example // a dot with initial progress -0.15 will run 2 frames // after a dot with initial progress -0.05 const initialProgress = -4 * Math.random(); // moves point to a random location and // resets its progress this.init = function() { this.progress = initialProgress; this.x = Math.random() * canvas.width; this.y = snowing ? initialProgress * canvas.height / 3 : Math.random() * canvas.height; this.r = snowing ? Math.random() * r : 0; this.rng = Math.random(); } this.draw = function() { if(this.progress >= 0) { ctx.beginPath(); // radius calculation: maps progress from [0, 1] to [0, pi], // then takes sine of that to get an increase, then decrease // in radius. absolute value to prevent floating point errors // accidentally causing negative sine values which cause ctx.arc // to throw errors if(!snowing) ctx.arc(this.x, this.y, Math.abs(Math.sin(Math.PI*this.progress)*r), 0, 2*Math.PI); else ctx.arc( this.x + 20 * Math.sin(this.progress * 3 * Math.PI) + Math.cos(this.progress * (5*this.rng) * Math.PI), this.y, this.r, 0, 2*Math.PI); ctx.fill(); } }; this.render = function() { if(snowing) { this.y += (Math.pow(this.rng, 0.25)) * 0.5 * (1 + Math.sin(this.progress % Math.PI)); this.x = (this.x + 0.3) % canvas.width; // wind this.progress += 0.005; this.draw(); if(this.y >= canvas.height + r) this.init(); } else { // stars come faster than they go // so user can look at them longer // i guess? idk this just looked pretty if(this.progress > 0.5) this.progress += 0.005; else this.progress += 0.05; this.draw(); if(this.progress >= 1) this.init(); } } } const dots = []; const n = 50; for(let i = 0; i < n; i++) { dots[i] = new Point(); dots[i].init(); } function loop() { ctx.clearRect(0, 0, canvas.width, canvas.height); for(let i = 0; i < n; i++) { dots[i].render(); } requestAnimationFrame(loop); } loop(); // slight convenience: fix the header section with my correct age automatically const birthday = { date: 19, month: 1, year: 2001, } const today = new Date(); let age = today.getFullYear() - birthday.year; if(today.getMonth() < birthday.month || (today.getMonth() == birthday.month && today.getDate() < birthday.date)) { --age; } const tens = ['', ' ten plus', ' twenty', ' thirty', ' forty', ' fifty', ' sixty', ' seventy', 'n eighty', ' ninety']; const ones = ['', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']; document.querySelector('.age').textContent = `${tens[Math.floor(age / 10)]} ${ones[age % 10]}`; // easter egg const sequences = [ 'CLAPOFF'.split('').map(s => 'Key' + s), 'CLAPON'.split('').map(s => 'Key' + s) ]; let eei = 0; addEventListener('keypress', e => { const clapper = document.querySelector('.clapper'); const sequence = sequences[darkMode]; if(e.code == sequence[eei]) { eei++; } else { eei = 0; } if(eei == sequence.length) { eei = 0; clapper.classList.toggle('clapping'); setTimeout(() => { if(!snowing) { if(darkMode) { document.body.classList.remove('dark-mode'); ctx.fillStyle = FILL_STYLES.light; darkMode = 0; } else { document.body.classList.add('dark-mode'); ctx.fillStyle = FILL_STYLES.dark; darkMode = 1; } eei = 0; } else { if(darkMode) { ctx.fillStyle = FILL_STYLES.snowing; document.body.style.background = BACKGROUNDS.evening; darkMode = 0; } else { ctx.fillStyle = FILL_STYLES.dark; document.body.style.background = BACKGROUNDS.night; darkMode = 1; } } setTimeout(() => clapper.classList.toggle('clapping'), 500); }, 500); } }); function among() { alert('haha among us'); } document.querySelector('.among').addEventListener('keypress', e => { if(e.code === 'Space') { e.preventDefault(); among(); } });