-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[client] Add Minimap Component to Display Worldmap Overview #1718
Changes from 20 commits
9dadf29
c783c34
a012a45
0350a58
e9f58ed
33f6ad5
0dfc995
7608301
556edad
9f3801b
1e33df2
45494dc
0ea6200
9cc9e74
cec73e4
fbb8d88
133aab5
0982d1f
f178350
25ca968
d528691
7c26d0d
36286f1
b67081d
6cbdeba
db6eab5
3666c6c
e3d6f1c
712a688
1cc61fb
76f8c34
6b6ac4a
8d40181
f459088
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
<!doctype html> | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
|
@@ -81,6 +81,12 @@ | |
</head> | ||
<body> | ||
<div id="root"></div> | ||
<canvas | ||
id="minimap" | ||
width="200" | ||
height="112" | ||
style="position: absolute; top: 10px; right: 10px; border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(0, 0, 0, 0.3); border-radius: 5px; z-index: 1000; display: none;" | ||
></canvas> | ||
<script type="module" src="/src/main.tsx"></script> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we have this in the Canvas and not in the react? |
||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,278 @@ | ||||||||||||||||||||||||||||||
import { FELT_CENTER } from "@/ui/config"; | ||||||||||||||||||||||||||||||
import { getHexForWorldPosition } from "@/ui/utils/utils"; | ||||||||||||||||||||||||||||||
import { throttle } from "lodash"; | ||||||||||||||||||||||||||||||
import * as THREE from "three"; | ||||||||||||||||||||||||||||||
import WorldmapScene from "../scenes/Worldmap"; | ||||||||||||||||||||||||||||||
import { ArmyManager } from "./ArmyManager"; | ||||||||||||||||||||||||||||||
import { Biome, BIOME_COLORS } from "./Biome"; | ||||||||||||||||||||||||||||||
import { StructureManager } from "./StructureManager"; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
const MINIMAP_CONFIG = { | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider moving this configuration object to a separate file for better maintainability and easier adjustments. |
||||||||||||||||||||||||||||||
MAP_COLS_WIDTH: 200, | ||||||||||||||||||||||||||||||
MAP_ROWS_HEIGHT: 100, | ||||||||||||||||||||||||||||||
COLORS: { | ||||||||||||||||||||||||||||||
ARMY: "#0000FF", | ||||||||||||||||||||||||||||||
STRUCTURE: "#FF0000", | ||||||||||||||||||||||||||||||
CAMERA: "#FFFFFF", | ||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||
SIZES: { | ||||||||||||||||||||||||||||||
STRUCTURE: 3, | ||||||||||||||||||||||||||||||
ARMY: 3, | ||||||||||||||||||||||||||||||
CAMERA: { | ||||||||||||||||||||||||||||||
TOP_SIDE_WIDTH_FACTOR: 105, | ||||||||||||||||||||||||||||||
BOTTOM_SIDE_WIDTH_FACTOR: 170, | ||||||||||||||||||||||||||||||
HEIGHT_FACTOR: 13, | ||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||
BORDER_WIDTH_PERCENT: 0.10, | ||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
class Minimap { | ||||||||||||||||||||||||||||||
private worldmapScene: WorldmapScene; | ||||||||||||||||||||||||||||||
private canvas: HTMLCanvasElement; | ||||||||||||||||||||||||||||||
private context: CanvasRenderingContext2D; | ||||||||||||||||||||||||||||||
private camera: THREE.PerspectiveCamera; | ||||||||||||||||||||||||||||||
private exploredTiles: Map<number, Set<number>>; | ||||||||||||||||||||||||||||||
private structureManager: StructureManager; | ||||||||||||||||||||||||||||||
private armyManager: ArmyManager; | ||||||||||||||||||||||||||||||
private biome: Biome; | ||||||||||||||||||||||||||||||
private displayRange: any = { | ||||||||||||||||||||||||||||||
minCol: 150, | ||||||||||||||||||||||||||||||
maxCol: 350, | ||||||||||||||||||||||||||||||
minRow: 100, | ||||||||||||||||||||||||||||||
maxRow: 200, | ||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||
private scaleX: number; | ||||||||||||||||||||||||||||||
private scaleY: number; | ||||||||||||||||||||||||||||||
private isDragging: boolean = false; | ||||||||||||||||||||||||||||||
private biomeCache: Map<string, string>; | ||||||||||||||||||||||||||||||
private scaledCoords: Map<string, { scaledCol: number, scaledRow: number }>; | ||||||||||||||||||||||||||||||
private BORDER_WIDTH_PERCENT = MINIMAP_CONFIG.BORDER_WIDTH_PERCENT; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
constructor( | ||||||||||||||||||||||||||||||
worldmapScene: WorldmapScene, | ||||||||||||||||||||||||||||||
exploredTiles: Map<number, Set<number>>, | ||||||||||||||||||||||||||||||
camera: THREE.PerspectiveCamera, | ||||||||||||||||||||||||||||||
structureManager: StructureManager, | ||||||||||||||||||||||||||||||
armyManager: ArmyManager, | ||||||||||||||||||||||||||||||
biome: Biome, | ||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||
this.worldmapScene = worldmapScene; | ||||||||||||||||||||||||||||||
this.canvas = document.getElementById("minimap") as HTMLCanvasElement; | ||||||||||||||||||||||||||||||
this.context = this.canvas.getContext("2d")!; | ||||||||||||||||||||||||||||||
this.structureManager = structureManager; | ||||||||||||||||||||||||||||||
this.exploredTiles = exploredTiles; | ||||||||||||||||||||||||||||||
this.armyManager = armyManager; | ||||||||||||||||||||||||||||||
this.biome = biome; | ||||||||||||||||||||||||||||||
this.camera = camera; | ||||||||||||||||||||||||||||||
this.scaleX = this.canvas.width / (this.displayRange.maxCol - this.displayRange.minCol); | ||||||||||||||||||||||||||||||
this.scaleY = this.canvas.height / (this.displayRange.maxRow - this.displayRange.minRow); | ||||||||||||||||||||||||||||||
this.biomeCache = new Map(); | ||||||||||||||||||||||||||||||
this.scaledCoords = new Map(); | ||||||||||||||||||||||||||||||
this.computeScaledCoords(); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The throttle value of 1000/30 (about 33ms) is a good choice for 30 FPS updates. However, you might want to make this configurable or tie it to the game's overall frame rate if that varies. |
||||||||||||||||||||||||||||||
this.draw = throttle(this.draw, 1000 / 30); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
this.canvas.addEventListener("click", this.handleClick); | ||||||||||||||||||||||||||||||
this.canvas.addEventListener("mousedown", this.handleMouseDown); | ||||||||||||||||||||||||||||||
this.canvas.addEventListener("mousemove", this.handleMouseMove); | ||||||||||||||||||||||||||||||
this.canvas.addEventListener("mouseup", this.handleMouseUp); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private computeScaledCoords() { | ||||||||||||||||||||||||||||||
this.scaledCoords.clear(); | ||||||||||||||||||||||||||||||
for (let col = this.displayRange.minCol; col <= this.displayRange.maxCol; col++) { | ||||||||||||||||||||||||||||||
for (let row = this.displayRange.minRow; row <= this.displayRange.maxRow; row++) { | ||||||||||||||||||||||||||||||
const scaledCol = (col - this.displayRange.minCol) * this.scaleX; | ||||||||||||||||||||||||||||||
const scaledRow = (row - this.displayRange.minRow) * this.scaleY; | ||||||||||||||||||||||||||||||
this.scaledCoords.set(`${col},${row}`, { scaledCol, scaledRow }); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private getMousePosition(event: MouseEvent) { | ||||||||||||||||||||||||||||||
const rect = this.canvas.getBoundingClientRect(); | ||||||||||||||||||||||||||||||
const x = event.clientX - rect.left; | ||||||||||||||||||||||||||||||
const y = event.clientY - rect.top; | ||||||||||||||||||||||||||||||
const col = Math.floor(x / this.scaleX) + this.displayRange.minCol; | ||||||||||||||||||||||||||||||
const row = Math.floor(y / this.scaleY) + this.displayRange.minRow; | ||||||||||||||||||||||||||||||
return { col, row, x, y }; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
draw() { | ||||||||||||||||||||||||||||||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); | ||||||||||||||||||||||||||||||
this.drawExploredTiles(); | ||||||||||||||||||||||||||||||
this.drawStructures(); | ||||||||||||||||||||||||||||||
this.drawArmies(); | ||||||||||||||||||||||||||||||
this.drawCamera(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private drawExploredTiles() { | ||||||||||||||||||||||||||||||
this.exploredTiles.forEach((rows, col) => { | ||||||||||||||||||||||||||||||
rows.forEach((row) => { | ||||||||||||||||||||||||||||||
const cacheKey = `${col},${row}`; | ||||||||||||||||||||||||||||||
let biomeColor; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
if (this.biomeCache.has(cacheKey)) { | ||||||||||||||||||||||||||||||
biomeColor = this.biomeCache.get(cacheKey)!; | ||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||
const biome = this.biome.getBiome(col + FELT_CENTER, row + FELT_CENTER); | ||||||||||||||||||||||||||||||
biomeColor = BIOME_COLORS[biome].getStyle(); | ||||||||||||||||||||||||||||||
this.biomeCache.set(cacheKey, biomeColor); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
if (this.scaledCoords.has(cacheKey)) { | ||||||||||||||||||||||||||||||
const { scaledCol, scaledRow } = this.scaledCoords.get(cacheKey)!; | ||||||||||||||||||||||||||||||
this.context.fillStyle = biomeColor; | ||||||||||||||||||||||||||||||
this.context.fillRect(scaledCol, scaledRow, this.scaleX, this.scaleY); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private drawStructures() { | ||||||||||||||||||||||||||||||
const allStructures = this.structureManager.structures.getStructures(); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
for (const [structureType, structures] of allStructures) { | ||||||||||||||||||||||||||||||
structures.forEach((structure) => { | ||||||||||||||||||||||||||||||
const { col, row } = structure.hexCoords; | ||||||||||||||||||||||||||||||
const cacheKey = `${col},${row}`; | ||||||||||||||||||||||||||||||
if (this.scaledCoords.has(cacheKey)) { | ||||||||||||||||||||||||||||||
const { scaledCol, scaledRow } = this.scaledCoords.get(cacheKey)!; | ||||||||||||||||||||||||||||||
this.context.fillStyle = MINIMAP_CONFIG.COLORS.STRUCTURE; | ||||||||||||||||||||||||||||||
this.context.fillRect(scaledCol, scaledRow, MINIMAP_CONFIG.SIZES.STRUCTURE, MINIMAP_CONFIG.SIZES.STRUCTURE); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private drawArmies() { | ||||||||||||||||||||||||||||||
const allArmies = this.armyManager.getArmies(); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
allArmies.forEach((army) => { | ||||||||||||||||||||||||||||||
const { x: col, y: row } = army.hexCoords.getNormalized(); | ||||||||||||||||||||||||||||||
const cacheKey = `${col},${row}`; | ||||||||||||||||||||||||||||||
if (this.scaledCoords.has(cacheKey)) { | ||||||||||||||||||||||||||||||
const { scaledCol, scaledRow } = this.scaledCoords.get(cacheKey)!; | ||||||||||||||||||||||||||||||
this.context.fillStyle = MINIMAP_CONFIG.COLORS.ARMY; | ||||||||||||||||||||||||||||||
this.context.fillRect(scaledCol, scaledRow, MINIMAP_CONFIG.SIZES.ARMY, MINIMAP_CONFIG.SIZES.ARMY); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
drawCamera() { | ||||||||||||||||||||||||||||||
const cameraPosition = this.camera.position; | ||||||||||||||||||||||||||||||
const { col, row } = getHexForWorldPosition(cameraPosition); | ||||||||||||||||||||||||||||||
const cacheKey = `${col},${row}`; | ||||||||||||||||||||||||||||||
if (this.scaledCoords.has(cacheKey)) { | ||||||||||||||||||||||||||||||
const { scaledCol, scaledRow } = this.scaledCoords.get(cacheKey)!; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
this.context.strokeStyle = MINIMAP_CONFIG.COLORS.CAMERA; | ||||||||||||||||||||||||||||||
this.context.beginPath(); | ||||||||||||||||||||||||||||||
const topSideWidth = (window.innerWidth / MINIMAP_CONFIG.SIZES.CAMERA.TOP_SIDE_WIDTH_FACTOR) * this.scaleX; | ||||||||||||||||||||||||||||||
const bottomSideWidth = (window.innerWidth / MINIMAP_CONFIG.SIZES.CAMERA.BOTTOM_SIDE_WIDTH_FACTOR) * this.scaleX; | ||||||||||||||||||||||||||||||
const height = MINIMAP_CONFIG.SIZES.CAMERA.HEIGHT_FACTOR * this.scaleY; | ||||||||||||||||||||||||||||||
this.context.moveTo(scaledCol - topSideWidth / 2, scaledRow - height); | ||||||||||||||||||||||||||||||
this.context.lineTo(scaledCol + topSideWidth / 2, scaledRow - height); | ||||||||||||||||||||||||||||||
this.context.lineTo(scaledCol + bottomSideWidth / 2, scaledRow); | ||||||||||||||||||||||||||||||
this.context.lineTo(scaledCol - bottomSideWidth / 2, scaledRow); | ||||||||||||||||||||||||||||||
this.context.lineTo(scaledCol - topSideWidth / 2, scaledRow - height); | ||||||||||||||||||||||||||||||
this.context.closePath(); | ||||||||||||||||||||||||||||||
this.context.lineWidth = 1; | ||||||||||||||||||||||||||||||
this.context.stroke(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
hideMinimap() { | ||||||||||||||||||||||||||||||
this.canvas.style.display = "none"; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
showMinimap() { | ||||||||||||||||||||||||||||||
this.canvas.style.display = "block"; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
moveMinimapCenterToUrlLocation() { | ||||||||||||||||||||||||||||||
const url = new URL(window.location.href); | ||||||||||||||||||||||||||||||
const col = parseInt(url.searchParams.get("col") || "0"); | ||||||||||||||||||||||||||||||
const row = parseInt(url.searchParams.get("row") || "0"); | ||||||||||||||||||||||||||||||
Comment on lines
+272
to
+273
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding error handling here in case the URL parameters are not present or not valid numbers.
Suggested change
|
||||||||||||||||||||||||||||||
this.displayRange.minCol = col - MINIMAP_CONFIG.MAP_COLS_WIDTH / 2; | ||||||||||||||||||||||||||||||
this.displayRange.maxCol = col + MINIMAP_CONFIG.MAP_COLS_WIDTH / 2; | ||||||||||||||||||||||||||||||
this.displayRange.minRow = row - MINIMAP_CONFIG.MAP_ROWS_HEIGHT / 2; | ||||||||||||||||||||||||||||||
this.displayRange.maxRow = row + MINIMAP_CONFIG.MAP_ROWS_HEIGHT / 2; | ||||||||||||||||||||||||||||||
this.computeScaledCoords(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
update() { | ||||||||||||||||||||||||||||||
this.draw(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private handleMouseDown = (event: MouseEvent) => { | ||||||||||||||||||||||||||||||
this.isDragging = true; | ||||||||||||||||||||||||||||||
this.moveCamera(event); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private handleMouseMove = (event: MouseEvent) => { | ||||||||||||||||||||||||||||||
if (this.isDragging) { | ||||||||||||||||||||||||||||||
this.moveCamera(event); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private handleMouseUp = () => { | ||||||||||||||||||||||||||||||
this.isDragging = false; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private moveCamera(event: MouseEvent) { | ||||||||||||||||||||||||||||||
const { col, row } = this.getMousePosition(event); | ||||||||||||||||||||||||||||||
this.worldmapScene.moveCameraToColRow(col, row, 0); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private moveMapRange(direction: string) { | ||||||||||||||||||||||||||||||
const colShift = (this.displayRange.maxCol - this.displayRange.minCol) / 4; | ||||||||||||||||||||||||||||||
const rowShift = (this.displayRange.maxRow - this.displayRange.minRow) / 4; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
switch (direction) { | ||||||||||||||||||||||||||||||
case 'left': | ||||||||||||||||||||||||||||||
this.displayRange.minCol -= colShift; | ||||||||||||||||||||||||||||||
this.displayRange.maxCol -= colShift; | ||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||
case 'right': | ||||||||||||||||||||||||||||||
this.displayRange.minCol += colShift; | ||||||||||||||||||||||||||||||
this.displayRange.maxCol += colShift; | ||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||
case 'top': | ||||||||||||||||||||||||||||||
this.displayRange.minRow -= rowShift; | ||||||||||||||||||||||||||||||
this.displayRange.maxRow -= rowShift; | ||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||
case 'bottom': | ||||||||||||||||||||||||||||||
this.displayRange.minRow += rowShift; | ||||||||||||||||||||||||||||||
this.displayRange.maxRow += rowShift; | ||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||
default: | ||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
this.scaleX = this.canvas.width / (this.displayRange.maxCol - this.displayRange.minCol); | ||||||||||||||||||||||||||||||
this.scaleY = this.canvas.height / (this.displayRange.maxRow - this.displayRange.minRow); | ||||||||||||||||||||||||||||||
this.computeScaledCoords(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
handleClick = (event: MouseEvent) => { | ||||||||||||||||||||||||||||||
event.stopPropagation(); | ||||||||||||||||||||||||||||||
const { col, row, x, y } = this.getMousePosition(event); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
const borderWidthX = this.canvas.width * this.BORDER_WIDTH_PERCENT; | ||||||||||||||||||||||||||||||
const borderWidthY = this.canvas.height * this.BORDER_WIDTH_PERCENT; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
if (x < borderWidthX) { | ||||||||||||||||||||||||||||||
this.moveMapRange('left'); | ||||||||||||||||||||||||||||||
} else if (x > this.canvas.width - borderWidthX) { | ||||||||||||||||||||||||||||||
this.moveMapRange('right'); | ||||||||||||||||||||||||||||||
} else if (y < borderWidthY) { | ||||||||||||||||||||||||||||||
this.moveMapRange('top'); | ||||||||||||||||||||||||||||||
} else if (y > this.canvas.height - borderWidthY) { | ||||||||||||||||||||||||||||||
this.moveMapRange('bottom'); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
this.worldmapScene.moveCameraToColRow(col, row, 0); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
export default Minimap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While adding the canvas element directly in the HTML is fine, consider creating it dynamically in JavaScript for more flexibility, especially if you plan to make the minimap size adjustable in the future.