your personal website on atproto - mirror blento.app

small fixes

Florian e9a0ea5f 6a64ae29

+13 -38
+12 -37
src/lib/cards/DinoGameCard/DinoGameCard.svelte src/lib/cards/GameCards/DinoGameCard/DinoGameCard.svelte
··· 1 1 <script lang="ts"> 2 - import type { ContentComponentProps } from '../types'; 2 + import type { ContentComponentProps } from '../../types'; 3 3 import { onMount, onDestroy } from 'svelte'; 4 4 5 5 let { item }: ContentComponentProps = $props(); ··· 16 16 // Sprite images (processed with transparent backgrounds) 17 17 let spritesLoaded = $state(false); 18 18 const sprites: Record<string, HTMLCanvasElement> = {}; 19 - let tilemap: HTMLImageElement | null = null; 20 19 21 20 // Tile size (original is 16x16) 22 21 const TILE_SIZE = 16; 23 - const TILEMAP_COLS = 20; 24 22 25 23 // Dynamic scaling - will be calculated based on canvas size 26 24 let scale = 2.5; ··· 88 86 }; 89 87 90 88 // Extract a tile from the tilemap and process it (white to black) 91 - function extractTile( 92 - img: HTMLImageElement, 93 - row: number, 94 - col: number 95 - ): HTMLCanvasElement { 89 + function extractTile(img: HTMLImageElement, row: number, col: number): HTMLCanvasElement { 96 90 const offscreen = document.createElement('canvas'); 97 91 offscreen.width = TILE_SIZE; 98 92 offscreen.height = TILE_SIZE; ··· 105 99 106 100 offCtx.drawImage(img, sx, sy, TILE_SIZE, TILE_SIZE, 0, 0, TILE_SIZE, TILE_SIZE); 107 101 108 - // Process: turn white to black for light mode 109 - const imageData = offCtx.getImageData(0, 0, TILE_SIZE, TILE_SIZE); 110 - const data = imageData.data; 111 - 112 - for (let i = 0; i < data.length; i += 4) { 113 - const r = data[i]; 114 - const g = data[i + 1]; 115 - const b = data[i + 2]; 116 - 117 - // Turn white/near-white pixels to black 118 - if (r > 220 && g > 220 && b > 220) { 119 - data[i] = 0; 120 - data[i + 1] = 0; 121 - data[i + 2] = 0; 122 - } 123 - } 124 - 125 - offCtx.putImageData(imageData, 0, 0); 126 102 return offscreen; 127 103 } 128 104 ··· 130 106 return new Promise<void>((resolve) => { 131 107 const img = new Image(); 132 108 img.onload = () => { 133 - tilemap = img; 134 - // Extract all sprites from the tilemap 135 109 for (const [key, pos] of Object.entries(SPRITE_POSITIONS)) { 136 110 sprites[key] = extractTile(img, pos.row, pos.col); 137 111 } ··· 485 459 // Draw game over text (no overlay background) 486 460 if (gameState === 'gameover') { 487 461 ctx.fillStyle = '#000000'; 488 - ctx.font = `bold ${Math.max(14, Math.floor(16 * (scale / 2.5)))}px monospace`; 462 + ctx.font = `bold ${Math.max(14, Math.floor(20 * (scale / 2.5)))}px monospace`; 489 463 ctx.textAlign = 'center'; 490 - ctx.fillText('GAME OVER', canvasWidth / 2, canvasHeight / 2 - 30); 464 + ctx.fillText('GAME OVER', canvasWidth / 2, canvasHeight / 2 - 40); 491 465 } 492 466 493 467 animationId = requestAnimationFrame(gameLoop); ··· 503 477 initGroundTiles(); 504 478 } 505 479 480 + let resizeObserver: ResizeObserver | undefined = $state(); 481 + 506 482 onMount(async () => { 507 483 ctx = canvas.getContext('2d'); 508 484 await loadSprites(); 509 485 resizeCanvas(); 510 486 511 - const resizeObserver = new ResizeObserver(() => { 487 + resizeObserver = new ResizeObserver(() => { 512 488 resizeCanvas(); 513 489 }); 514 490 resizeObserver.observe(canvas.parentElement!); 515 491 516 492 gameLoop(); 517 - 518 - return () => { 519 - resizeObserver.disconnect(); 520 - }; 521 493 }); 522 494 523 495 onDestroy(() => { 496 + resizeObserver?.disconnect(); 497 + 524 498 if (animationId) { 525 499 cancelAnimationFrame(animationId); 526 500 } ··· 530 504 <svelte:window onkeydown={handleKeyDown} onkeyup={handleKeyUp} /> 531 505 532 506 <div class="relative h-full w-full overflow-hidden"> 533 - <canvas bind:this={canvas} class="h-full w-full dark:invert" ontouchstart={handleTouch}></canvas> 507 + <canvas bind:this={canvas} class="h-full w-full invert dark:invert-0" ontouchstart={handleTouch} 508 + ></canvas> 534 509 535 510 {#if gameState === 'idle' || gameState === 'gameover'} 536 511 <button 537 512 onclick={startGame} 538 - class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform cursor-pointer rounded-lg border-2 border-black bg-white/30 px-6 py-3 font-mono text-sm font-bold text-black transition-colors hover:bg-black hover:text-white" 513 + class="bg-base-50/80 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform cursor-pointer rounded-lg border-2 border-black px-6 py-3 font-mono font-bold text-black transition-colors duration-200 hover:bg-black hover:text-white" 539 514 > 540 515 {gameState === 'gameover' ? 'PLAY AGAIN' : 'START'} 541 516 </button>
src/lib/cards/DinoGameCard/SidebarItemDinoGameCard.svelte src/lib/cards/GameCards/DinoGameCard/SidebarItemDinoGameCard.svelte
src/lib/cards/DinoGameCard/index.ts src/lib/cards/GameCards/DinoGameCard/index.ts
+1 -1
src/lib/cards/index.ts
··· 3 3 import { BigSocialCardDefinition } from './BigSocialCard'; 4 4 import { BlueskyMediaCardDefinition } from './BlueskyMediaCard'; 5 5 import { BlueskyPostCardDefinition } from './BlueskyPostCard'; 6 - import { DinoGameCardDefinition } from './DinoGameCard'; 6 + import { DinoGameCardDefinition } from './GameCards/DinoGameCard'; 7 7 import { EmbedCardDefinition } from './EmbedCard'; 8 8 import { ImageCardDefinition } from './ImageCard'; 9 9 import { LinkCardDefinition } from './LinkCard';