Add a coordinate system and mouse tracking to the canvas

Adds X and Y axis labels to the canvas, displays mouse coordinates on hover, and updates the project documentation to reflect these new features.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 0385ea33-cde8-4bbd-8fce-8d192d30eb41
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/870d08ce-da3b-4822-9874-c2fe2b7628b1/0385ea33-cde8-4bbd-8fce-8d192d30eb41/zNDeQGA
This commit is contained in:
freesemar93
2025-08-18 12:28:14 +00:00
parent 5f88d407ee
commit a3e07b5898
3 changed files with 76 additions and 7 deletions

View File

@@ -24,6 +24,7 @@ export function Canvas({
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [zoom, setZoom] = useState(1); const [zoom, setZoom] = useState(1);
const [pixelSize, setPixelSize] = useState(8); const [pixelSize, setPixelSize] = useState(8);
const [mouseCoords, setMouseCoords] = useState<{x: number, y: number} | null>(null);
// Create pixel map for O(1) lookup // Create pixel map for O(1) lookup
const pixelMap = new Map<string, string>(); const pixelMap = new Map<string, string>();
@@ -36,6 +37,14 @@ export function Canvas({
onPixelClick(x, y); onPixelClick(x, y);
}; };
const handlePixelMouseEnter = (x: number, y: number) => {
setMouseCoords({ x, y });
};
const handlePixelMouseLeave = () => {
setMouseCoords(null);
};
const handleZoomIn = () => { const handleZoomIn = () => {
setZoom(prev => Math.min(prev * 1.2, 3)); setZoom(prev => Math.min(prev * 1.2, 3));
}; };
@@ -67,11 +76,49 @@ export function Canvas({
className="w-full h-full overflow-auto p-8" className="w-full h-full overflow-auto p-8"
data-testid="canvas-container" data-testid="canvas-container"
> >
<div {/* Coordinate System Container */}
className="grid mx-auto border border-gray-400 relative" <div className="relative inline-block">
style={canvasStyle} {/* Top X-axis coordinates */}
data-testid="pixel-canvas" <div className="flex ml-8 mb-1">
> {Array.from({ length: Math.ceil(canvasWidth / 10) }, (_, i) => (
<div
key={i}
className="text-xs text-gray-400 text-center"
style={{
width: `${10 * pixelSize}px`,
fontSize: `${Math.max(8, pixelSize * 0.8)}px`
}}
>
{i * 10}
</div>
))}
</div>
{/* Canvas with left Y-axis coordinates */}
<div className="flex">
{/* Left Y-axis coordinates */}
<div className="flex flex-col mr-1">
{Array.from({ length: Math.ceil(canvasHeight / 10) }, (_, i) => (
<div
key={i}
className="text-xs text-gray-400 flex items-center justify-end"
style={{
height: `${10 * pixelSize}px`,
width: '24px',
fontSize: `${Math.max(8, pixelSize * 0.8)}px`
}}
>
{i * 10}
</div>
))}
</div>
{/* Main Canvas */}
<div
className="grid border border-gray-400 relative"
style={canvasStyle}
data-testid="pixel-canvas"
>
{Array.from({ length: canvasHeight }, (_, y) => {Array.from({ length: canvasHeight }, (_, y) =>
Array.from({ length: canvasWidth }, (_, x) => { Array.from({ length: canvasWidth }, (_, x) => {
const pixelColor = pixelMap.get(`${x},${y}`) || "#FFFFFF"; const pixelColor = pixelMap.get(`${x},${y}`) || "#FFFFFF";
@@ -88,6 +135,8 @@ export function Canvas({
height: `${pixelSize}px` height: `${pixelSize}px`
}} }}
onClick={() => handlePixelClick(x, y)} onClick={() => handlePixelClick(x, y)}
onMouseEnter={() => handlePixelMouseEnter(x, y)}
onMouseLeave={handlePixelMouseLeave}
data-testid={`pixel-${x}-${y}`} data-testid={`pixel-${x}-${y}`}
data-x={x} data-x={x}
data-y={y} data-y={y}
@@ -95,9 +144,26 @@ export function Canvas({
); );
}) })
)} )}
</div>
</div>
</div> </div>
</div> </div>
{/* Coordinate Info Display */}
<div className="absolute top-4 left-4 bg-panel-bg px-3 py-2 rounded-lg border border-gray-600">
<div className="text-sm text-gray-300">
Canvas: {canvasWidth} × {canvasHeight}
</div>
<div className="text-xs text-gray-400">
Zoom: {Math.round(zoom * 100)}%
</div>
{mouseCoords && (
<div className="text-xs text-green-400 mt-1">
Mouse: ({mouseCoords.x}, {mouseCoords.y})
</div>
)}
</div>
{/* Zoom Controls */} {/* Zoom Controls */}
<div className="absolute bottom-4 right-4 flex flex-col space-y-2"> <div className="absolute bottom-4 right-4 flex flex-col space-y-2">
<button <button

View File

@@ -0,0 +1 @@
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="#FFFFFF"/><rect x="64" y="7" width="1" height="1" fill="#be0039"/><rect x="65" y="7" width="1" height="1" fill="#00ccc0"/></svg>

After

Width:  |  Height:  |  Size: 232 B

View File

@@ -45,10 +45,12 @@ Language: German (Deutsch) - User communicates in German and prefers responses i
**User Experience Features** **User Experience Features**
- Interactive canvas with zoom and pan capabilities - Interactive canvas with zoom and pan capabilities
- Color palette with 16 predefined colors - Official r/place 2022 color palette with 32 authentic colors arranged in 8x4 grid
- Coordinate system with X/Y axis labels showing every 10th position
- Real-time mouse coordinate tracking and display
- Cooldown system to prevent pixel spam - Cooldown system to prevent pixel spam
- Recent placements tracking and display - Recent placements tracking and display
- Grid overlay toggle for precise pixel placement - Canvas size and zoom level information display
- Toast notifications for user feedback - Toast notifications for user feedback
## External Dependencies ## External Dependencies