diff --git a/public/fishie.png b/public/lightbar-images/fishie.png similarity index 100% rename from public/fishie.png rename to public/lightbar-images/fishie.png diff --git a/public/lightbar-images/snowflake.svg b/public/lightbar-images/snowflake.svg new file mode 100644 index 00000000..50d9c382 --- /dev/null +++ b/public/lightbar-images/snowflake.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg fill="#fff" height="800px" width="800px" version="1.1" id="Capa_1" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" + viewBox="0 0 298 298" xml:space="preserve"> +<g> + <path d="M289.5,140.5h-24.606l11.031-11.03c2.93-2.929,2.93-7.678,0.001-10.606c-2.929-2.929-7.678-2.93-10.606-0.001 + L243.681,140.5h-36.369l16.182-17.392c2.821-3.032,2.65-7.777-0.383-10.6c-1.243-1.156-2.775-1.802-4.345-1.961 + c-0.952-0.047-21.495-0.003-21.495-0.003L221.315,86.5H251.5c4.143,0,7.5-3.357,7.5-7.5s-3.357-7.5-7.5-7.5h-15.186l17.69-17.69 + c2.929-2.93,2.929-7.678,0-10.608c-2.93-2.928-7.844-2.928-10.774,0L225.167,61.1V45.5c0-4.143-3.357-7.5-7.5-7.5 + c-4.143,0-7.5,3.357-7.5,7.5v30.601l-24.837,25.004l-0.415-22.645c-0.001-0.036,0.035-0.07,0.034-0.106 + c-0.035-1.824-0.704-3.641-2.07-5.059c-2.873-2.982-7.778-3.07-10.761-0.194l-15.951,15.226V53.107l21.47-21.304 + c2.929-2.93,3.012-7.678,0.083-10.607c-2.93-2.928-7.803-2.928-10.732,0l-10.821,10.696V7.5c0-4.143-3.357-7.5-7.5-7.5 + c-4.143,0-7.5,3.357-7.5,7.5v24.393l-10.53-10.696c-2.93-2.928-7.594-2.928-10.524,0c-2.929,2.93-3.054,7.678-0.125,10.607 + l21.179,21.304v35.421l-16.176-15.475c-3.009-2.847-7.67-2.718-10.52,0.289c-1.075,1.136-1.683,2.52-1.914,3.955 + c-0.142,0.583-0.203,1.188-0.201,1.811l-0.088,21.229l-25.1-24.944V45.5c0-4.143-3.357-7.5-7.5-7.5s-7.5,3.357-7.5,7.5v14.894 + L55.142,43.202c-2.93-2.928-7.594-2.928-10.524,0c-2.929,2.93-2.887,7.678,0.042,10.608L62.392,71.5H46.5 + c-4.143,0-7.5,3.357-7.5,7.5s3.357,7.5,7.5,7.5h30.892l24.744,24.744l-23.057,0.831c-4.021,0.146-7.524,3.435-7.563,7.418 + c-0.004,0.112-0.349,0.225-0.349,0.337c0,0.003,0,0.007,0,0.011c0,0.008,0.345,0.017,0.345,0.024 + c0.045,1.875,0.955,3.736,2.395,5.158L89.748,140.5H55.025l-21.638-21.638c-2.93-2.928-7.678-2.928-10.607,0 + c-2.929,2.93-2.929,7.678,0,10.607l11.03,11.03H8.5c-4.143,0-7.5,3.357-7.5,7.5s3.357,7.5,7.5,7.5h25.02L22.78,166.239 + c-2.929,2.93-2.929,7.678,0,10.607c1.465,1.464,3.385,2.196,5.304,2.196c1.919,0,3.839-0.732,5.304-2.196L54.734,155.5h35.027 + l-15.253,16.394c-2.821,3.032-2.65,7.777,0.383,10.6c1.444,1.344,3.277,2.009,5.106,2.009c0.034,0,0.068-0.005,0.103-0.005 + c0.022,0,0.044,0.003,0.065,0.003c0.018,0,0.037,0,0.055,0l22.005-0.125L77.101,209.5H46.5c-4.143,0-7.5,3.357-7.5,7.5 + s3.357,7.5,7.5,7.5h15.601l-17.399,17.399c-2.929,2.93-2.929,7.678,0,10.607c1.465,1.464,3.385,2.196,5.304,2.196 + c1.919,0,3.672-0.732,5.137-2.196l17.025-17.191V250.5c0,4.143,3.357,7.5,7.5,7.5s7.5-3.357,7.5-7.5v-30.185l25.445-25.278 + l0.977,24.39c0.148,4.046,3.517,7.306,7.532,7.225c1.364-0.027,2.844-0.465,4.312-1.543c1.063-0.781,15.734-15.812,15.734-15.812 + v35.385l-20.971,21.137c-2.93,2.929-2.846,7.678,0.082,10.607c1.465,1.465,3.425,2.197,5.345,2.197 + c1.919,0,3.693-0.732,5.157-2.196l10.387-10.532V290.5c0,4.143,3.357,7.5,7.5,7.5c4.143,0,7.5-3.357,7.5-7.5v-25.31l11.404,11.237 + c1.465,1.464,3.468,2.196,5.387,2.196c1.919,0,3.881-0.732,5.345-2.196c2.929-2.93,2.783-7.678-0.146-10.607l-21.99-21.845v-35.7 + c0,0,13.729,12.896,15.896,14.976c2.167,2.08,3.942,3.25,6.525,3.25c0.015,0,0.03,0,0.046,0c4.142,0,7.48-3.604,7.455-7.746 + l-0.306-23.696l24.384,24.551V250.5c0,4.143,3.357,7.5,7.5,7.5c4.143,0,7.5-3.357,7.5-7.5v-15.891l18.064,17.897 + c1.465,1.464,3.467,2.196,5.387,2.196c1.919,0,3.88-0.732,5.345-2.196c2.929-2.93,2.95-7.678,0.021-10.607L236.605,224.5H251.5 + c4.143,0,7.5-3.357,7.5-7.5s-3.357-7.5-7.5-7.5h-29.894l-25.742-25.742l23.059-0.831c0.082-0.003,0.162-0.016,0.243-0.021 + c0.03-0.002,0.06-0.005,0.09-0.008c3.977-0.319,7.037-3.709,6.892-7.736c-0.087-2.424-1.32-4.531-3.155-5.837L209.138,155.5h34.835 + l21.345,21.346c1.465,1.465,3.384,2.197,5.304,2.197c1.919,0,3.839-0.732,5.303-2.196c2.93-2.929,2.93-7.678,0.001-10.606 + l-10.74-10.74H289.5c4.143,0,7.5-3.357,7.5-7.5S293.643,140.5,289.5,140.5z M200.795,125.483L186.823,140.5h-19.507l15.002-15.002 + L200.795,125.483z M170.21,95.784l0.356,20.002l-14.399,14.315V109.16L170.21,95.784z M127.263,95.865l13.904,13.323v20.205 + l-13.925-14.008L127.263,95.865z M96.862,126.444l19.762-0.712l14.768,14.768h-20.299L96.862,126.444z M97.246,169.477 + L110.25,155.5h20.851l-13.841,13.841L97.246,169.477z M127.863,201.599l-0.854-21.042l14.158-14.241v21.604L127.863,201.599z + M170.819,201.264l-14.652-13.478v-22.179l14.442,14.359L170.819,201.264z M200.991,168.564l-19.614,0.706l-13.77-13.77h20.292 + L200.991,168.564z"/> +</g> +</svg> \ No newline at end of file diff --git a/src/components/utils/Lightbar.tsx b/src/components/utils/Lightbar.tsx index 6ef810a8..df9e5505 100644 --- a/src/components/utils/Lightbar.tsx +++ b/src/components/utils/Lightbar.tsx @@ -1,6 +1,12 @@ import { useEffect, useRef } from "react"; import "./Lightbar.css"; +interface LightbarOptions { + imgSrc?: string; + horizontalMotion?: boolean; + sizeRange?: [number, number]; +} + class Particle { x = 0; @@ -18,11 +24,23 @@ class Particle { image: null | HTMLImageElement = null; - constructor(canvas: HTMLCanvasElement, { doFish } = { doFish: false }) { - if (doFish) { - this.image = new Image(); - if (this.image) this.image.src = "/fishie.png"; + size = 10; + + options: LightbarOptions; + + constructor( + canvas: HTMLCanvasElement, + options: LightbarOptions = { + horizontalMotion: false, + sizeRange: [10, 10], } + ) { + if (options.imgSrc) { + this.image = new Image(); + this.image.src = options.imgSrc; + } + + this.options = options; this.reset(canvas); this.initialize(canvas); @@ -39,7 +57,13 @@ class Particle { const second = 60; this.lifetime = second * 3 + Math.random() * (second * 30); - if (this.image) { + this.size = this.options.sizeRange + ? Math.random() * + (this.options.sizeRange[1] - this.options.sizeRange[0]) + + this.options.sizeRange[0] + : 10; + + if (this.options.horizontalMotion) { this.direction = Math.random() <= 0.5 ? 0 : Math.PI; this.lifetime = 30 * second; } @@ -81,7 +105,7 @@ class Particle { if (this.image) { ctx.translate(this.x, this.y); - const w = 10; + const w = this.size; const h = (this.image.naturalWidth / this.image.naturalHeight) * w; ctx.rotate(this.direction - Math.PI); ctx.drawImage(this.image, -w / 2, h, h, w); @@ -113,12 +137,36 @@ function ParticlesCanvas() { canvas.width = canvas.scrollWidth; canvas.height = canvas.scrollHeight; - const shouldShowFishie = Math.floor(Math.random() * 600) === 1; + // Basic particle config const particleCount = 20; + let imageParticleCount = particleCount; + // Holiday overrides + let imageOverride; + let sizeRange; + const date = new Date(); + const month = date.getMonth(); + const day = date.getDate(); + if (month === 11 && day >= 24 && day <= 26) { + imageOverride = "/lightbar-images/snowflake.svg"; + sizeRange = [4, 15] as [number, number]; + } + + // Fish easter egg + const shouldShowFishie = Math.floor(Math.random() * 600) === 1; + if (shouldShowFishie) { + imageOverride = "/lightbar-images/fishie.png"; + imageParticleCount = particleCount / 2; + sizeRange = [10, 11] as [number, number]; + } + + // HOIST THE SAIL (of particles)! for (let i = 0; i < particleCount; i += 1) { + const isImageParticle = imageOverride && i <= imageParticleCount; const particle = new Particle(canvas, { - doFish: shouldShowFishie && i <= particleCount / 2, + imgSrc: isImageParticle ? imageOverride : undefined, + horizontalMotion: imageOverride?.includes("fishie"), + sizeRange, }); particles.push(particle); } @@ -161,8 +209,8 @@ function ParticlesCanvas() { export function Lightbar(props: { className?: string }) { return ( - <div className="absolute inset-0 w-full h-[calc(680px)] overflow-hidden pointer-events-none -mt-64"> - <div className="max-w-screen w-full h-[calc(680px)] relative pt-64"> + <div className="absolute inset-0 w-full h-[680px] overflow-hidden pointer-events-none -mt-64"> + <div className="max-w-screen w-full h-[680px] relative pt-64"> <div className={props.className}> <div className="lightbar"> <ParticlesCanvas />