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