Mercurial
comparison mrjunejune/src/public/dog-game.js @ 240:c345083cd8a7
[mrjunejune] Show star animation in light mode
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 03 Aug 2026 10:48:59 -0700 |
| parents | 84826b3c655b |
| children | 9c2eec61a152 |
comparison
equal
deleted
inserted
replaced
| 239:40fa2363fee1 | 240:c345083cd8a7 |
|---|---|
| 18 // Load treat image | 18 // Load treat image |
| 19 const treatImage = new Image(); | 19 const treatImage = new Image(); |
| 20 treatImage.src = "/public/dog-treat.png"; | 20 treatImage.src = "/public/dog-treat.png"; |
| 21 | 21 |
| 22 // Load star images for background | 22 // Load star images for background |
| 23 const lightStarColors = ["#315a91", "#8f3f63", "#34705f", "#76519b"]; | |
| 23 let starImages = [ | 24 let starImages = [ |
| 24 { src: "/public/start-large.png", img: new Image() }, | 25 { src: "/public/start-large.png", img: new Image(), lightImg: null }, |
| 25 { src: "/public/start-small-1.png", img: new Image() }, | 26 { src: "/public/start-small-1.png", img: new Image(), lightImg: null }, |
| 26 { src: "/public/start-small-2.png", img: new Image() }, | 27 { src: "/public/start-small-2.png", img: new Image(), lightImg: null }, |
| 27 { src: "/public/start-small-3.png", img: new Image() } | 28 { src: "/public/start-small-3.png", img: new Image(), lightImg: null } |
| 28 ]; | 29 ]; |
| 29 starImages.forEach(star => star.img.src = star.src); | 30 starImages.forEach((star, index) => { |
| 31 star.img.onload = () => { | |
| 32 const tinted = document.createElement("canvas"); | |
| 33 tinted.width = star.img.naturalWidth; | |
| 34 tinted.height = star.img.naturalHeight; | |
| 35 const tintedCtx = tinted.getContext("2d"); | |
| 36 tintedCtx.drawImage(star.img, 0, 0); | |
| 37 tintedCtx.globalCompositeOperation = "source-in"; | |
| 38 tintedCtx.fillStyle = lightStarColors[index]; | |
| 39 tintedCtx.fillRect(0, 0, tinted.width, tinted.height); | |
| 40 star.lightImg = tinted; | |
| 41 updateStarPalette(); | |
| 42 }; | |
| 43 star.img.src = star.src; | |
| 44 }); | |
| 30 | 45 |
| 31 const background = document.getElementById('background'); | 46 const background = document.getElementById('background'); |
| 32 const bgCtx = background.getContext("2d"); | 47 const bgCtx = background.getContext("2d"); |
| 33 const ctx = game.getContext("2d"); | 48 const ctx = game.getContext("2d"); |
| 34 const uiCtx = gameUI.getContext("2d"); | 49 const uiCtx = gameUI.getContext("2d"); |
| 50 const darkModePreference = window.matchMedia("(prefers-color-scheme: dark)"); | |
| 51 | |
| 52 function isDarkMode() { | |
| 53 const root = document.documentElement; | |
| 54 if (root.classList.contains("dark")) return true; | |
| 55 if (root.classList.contains("light-mode")) return false; | |
| 56 return darkModePreference.matches; | |
| 57 } | |
| 58 | |
| 59 function starImageForTheme(imageIndex) { | |
| 60 const star = starImages[imageIndex]; | |
| 61 return isDarkMode() ? star.img : star.lightImg || star.img; | |
| 62 } | |
| 63 | |
| 64 function updateStarPalette() { | |
| 65 stars.forEach(star => { | |
| 66 star.image = starImageForTheme(star.imageIndex); | |
| 67 }); | |
| 68 } | |
| 35 | 69 |
| 36 | 70 |
| 37 // Dog initial position and movement | 71 // Dog initial position and movement |
| 38 const initialDogVx = 2; | 72 const initialDogVx = 2; |
| 39 let x = Math.random() * (game.width); | 73 let x = Math.random() * (game.width); |
| 64 function createStars() { | 98 function createStars() { |
| 65 stars = []; | 99 stars = []; |
| 66 const starCount = window.innerWidth * 0.05; | 100 const starCount = window.innerWidth * 0.05; |
| 67 | 101 |
| 68 for (let i = 0; i < starCount; i++) { | 102 for (let i = 0; i < starCount; i++) { |
| 69 const starType = starImages[Math.floor(Math.random() * starImages.length)]; | 103 const imageIndex = Math.floor(Math.random() * starImages.length); |
| 104 const starType = starImages[imageIndex]; | |
| 70 const scale = 0.5 + Math.random() * 0.8; // Random scale | 105 const scale = 0.5 + Math.random() * 0.8; // Random scale |
| 71 | 106 |
| 72 stars.push({ | 107 stars.push({ |
| 73 x: Math.random() * background.width, | 108 x: Math.random() * background.width, |
| 74 y: Math.random() * background.height, | 109 y: Math.random() * background.height, |
| 75 vx: (Math.random() - 0.5) * 0.5, | 110 vx: (Math.random() - 0.5) * 0.5, |
| 76 vy: (Math.random() - 0.5) * 0.5, | 111 vy: (Math.random() - 0.5) * 0.5, |
| 77 rotation: Math.random() * Math.PI * 2, | 112 rotation: Math.random() * Math.PI * 2, |
| 78 rotationSpeed: (Math.random() - 0.5) * 0.02, | 113 rotationSpeed: (Math.random() - 0.5) * 0.02, |
| 79 image: starType.img, | 114 imageIndex: imageIndex, |
| 115 image: starImageForTheme(imageIndex), | |
| 80 size: starType.img.width, | 116 size: starType.img.width, |
| 81 scale: scale, | 117 scale: scale, |
| 82 opacity: 0.3 + Math.random() * 0.4 // Random opacity | 118 opacity: 0.3 + Math.random() * 0.4 // Random opacity |
| 83 }); | 119 }); |
| 84 } | 120 } |
| 284 createStars(); // Recreate stars on resize | 320 createStars(); // Recreate stars on resize |
| 285 drawUI(); // Redraw UI after resize | 321 drawUI(); // Redraw UI after resize |
| 286 } | 322 } |
| 287 resizeCanvas(); | 323 resizeCanvas(); |
| 288 window.addEventListener('resize', resizeCanvas); | 324 window.addEventListener('resize', resizeCanvas); |
| 325 new MutationObserver(updateStarPalette).observe( | |
| 326 document.documentElement, | |
| 327 { attributes: true, attributeFilter: ["class"] } | |
| 328 ); | |
| 329 darkModePreference.addEventListener("change", updateStarPalette); | |
| 289 | 330 |
| 290 window.onload = () => { | 331 window.onload = () => { |
| 291 resizeCanvas(); // ensure canvas has real size | 332 resizeCanvas(); // ensure canvas has real size |
| 292 createStars(); // stars need correct canvas size | 333 createStars(); // stars need correct canvas size |
| 293 animateBackground(); // safe to start background animation | 334 animateBackground(); // safe to start background animation |
| 294 }; | 335 }; |
| 295 |