A browser source overlay for winter vibes for your Live Streams or Videos
at main 2.5 kB view raw
1import Config from "./config.js"; 2import { createSnowflake, drawSnowflake, moveSnowflake } from "./snowflake.js"; 3import { 4 accumulateSnow, 5 snowAccumulatorCollisionY, 6 createSnowAccumulator, 7 drawSnowAccumulators, 8 resetSnowAccumulator, 9} from "./snow_accumulator.js"; 10import { createPlow, drawPlow, movePlow, plowDone } from "./plow.js"; 11import "./contextMenu.js"; 12 13/** 14 * @type{Array.<Snowflake>} 15 */ 16const snowFlakes = []; 17 18/** 19 * @type SnowAccumulator[] 20 */ 21const floor = []; 22 23const sectionSize = Config.canvas.width / Config.groundAccumulator.slices; 24/** 25 * @type {HTMLCanvasElement} 26 */ 27let canvas; 28 29/** 30 * @type {CanvasRenderingContext2D} 31 */ 32let ctx; 33 34/** 35 * @type {Plow} 36 */ 37let plow; 38 39let lastSpawn = 0; 40let lastFrame = 0; 41 42let isPlowing = false; 43 44function init() { 45 // @ts-ignore -- I dunno how to get ts / jsdoc to be ok with this 46 canvas = document.getElementById("main"); 47 48 ctx = canvas.getContext("2d"); 49 canvas.width = Config.canvas.width; 50 canvas.height = Config.canvas.height; 51 ctx.imageSmoothingEnabled = false; 52 53 for (let i = 0; i < Config.groundAccumulator.slices; i++) { 54 floor.push( 55 createSnowAccumulator(i * sectionSize, Config.canvas.height, sectionSize), 56 ); 57 } 58 59 window.requestAnimationFrame(gameLoop); 60} 61 62/** 63 * @param time {DOMHighResTimeStamp} 64 */ 65function gameLoop(time) { 66 const delta = time - lastFrame; 67 68 ctx.clearRect(0, 0, Config.canvas.width, Config.canvas.height); 69 if (time - lastSpawn > 500 && snowFlakes.length < Config.snow.max) { 70 for (let i = 0; i < Math.floor(Math.random() * 2); i++) { 71 snowFlakes.push(createSnowflake()); 72 } 73 } 74 75 snowFlakes.forEach((flake, idx) => { 76 moveSnowflake(flake, delta); 77 const floorIdx = Math.floor(flake.x / sectionSize); 78 79 if (snowAccumulatorCollisionY(floor[floorIdx], flake)) { 80 if (!isPlowing) { 81 accumulateSnow(floor[floorIdx], flake); 82 } 83 snowFlakes[idx] = createSnowflake(); 84 } 85 drawSnowflake(flake, ctx); 86 }); 87 88 const avgHeight = 89 floor.reduce((acc, sa) => acc + sa.height, 0) / floor.length; 90 91 if (avgHeight > Config.groundAccumulator.max) { 92 isPlowing = true; 93 } 94 95 drawSnowAccumulators(ctx, floor, plow); 96 97 if (isPlowing) { 98 plow ??= createPlow(); 99 movePlow(plow, delta); 100 drawPlow(plow, ctx, time); 101 if (plowDone(plow)) { 102 isPlowing = false; 103 plow = null; 104 floor.forEach((sa) => resetSnowAccumulator(sa)); 105 } 106 } 107 108 lastFrame = time; 109 window.requestAnimationFrame(gameLoop); 110} 111 112init();