@@ -17,6 +17,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
|
|||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [dragStart, setDragStart] = useState<Position>({ x: 0, y: 0 });
|
const [dragStart, setDragStart] = useState<Position>({ x: 0, y: 0 });
|
||||||
const [canvasSize, setCanvasSize] = useState({ width, height });
|
const [canvasSize, setCanvasSize] = useState({ width, height });
|
||||||
|
const [zoom, setZoom] = useState(1);
|
||||||
|
|
||||||
// Handle responsive canvas sizing
|
// Handle responsive canvas sizing
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -35,12 +36,51 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
|
|||||||
return () => window.removeEventListener('resize', updateSize);
|
return () => window.removeEventListener('resize', updateSize);
|
||||||
}, [width, height]);
|
}, [width, height]);
|
||||||
|
|
||||||
// Auto-center the board on first tile
|
// Auto-zoom and auto-center to fit all tiles
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (placedTiles.length === 1 && offset.x === 0 && offset.y === 0) {
|
if (placedTiles.length === 0) return;
|
||||||
setOffset({ x: canvasSize.width / 2 - 400, y: canvasSize.height / 2 - 300 });
|
|
||||||
}
|
// Calculate bounding box of all tiles
|
||||||
}, [placedTiles.length, canvasSize.width, canvasSize.height, offset]);
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
||||||
|
|
||||||
|
placedTiles.forEach(placedTile => {
|
||||||
|
const { position, orientation } = placedTile;
|
||||||
|
const tileWidth = orientation === 'horizontal' ? 60 : 30;
|
||||||
|
const tileHeight = orientation === 'horizontal' ? 30 : 60;
|
||||||
|
|
||||||
|
minX = Math.min(minX, position.x);
|
||||||
|
minY = Math.min(minY, position.y);
|
||||||
|
maxX = Math.max(maxX, position.x + tileWidth);
|
||||||
|
maxY = Math.max(maxY, position.y + tileHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add padding
|
||||||
|
const padding = 50;
|
||||||
|
minX -= padding;
|
||||||
|
minY -= padding;
|
||||||
|
maxX += padding;
|
||||||
|
maxY += padding;
|
||||||
|
|
||||||
|
const contentWidth = maxX - minX;
|
||||||
|
const contentHeight = maxY - minY;
|
||||||
|
|
||||||
|
// Calculate zoom to fit all tiles in viewport
|
||||||
|
const isMobile = window.innerWidth < 640;
|
||||||
|
const zoomX = canvasSize.width / contentWidth;
|
||||||
|
const zoomY = canvasSize.height / contentHeight;
|
||||||
|
const newZoom = Math.min(zoomX, zoomY, isMobile ? 1 : 1.5); // Limit max zoom
|
||||||
|
|
||||||
|
// Calculate center position
|
||||||
|
const centerX = (minX + maxX) / 2;
|
||||||
|
const centerY = (minY + maxY) / 2;
|
||||||
|
|
||||||
|
// Set offset to center the content
|
||||||
|
const newOffsetX = canvasSize.width / 2 - centerX * newZoom;
|
||||||
|
const newOffsetY = canvasSize.height / 2 - centerY * newZoom;
|
||||||
|
|
||||||
|
setZoom(newZoom);
|
||||||
|
setOffset({ x: newOffsetX, y: newOffsetY });
|
||||||
|
}, [placedTiles, canvasSize.width, canvasSize.height]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvas = canvasRef.current;
|
const canvas = canvasRef.current;
|
||||||
@@ -78,28 +118,30 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
|
|||||||
const tileWidth = orientation === 'horizontal' ? 60 : 30;
|
const tileWidth = orientation === 'horizontal' ? 60 : 30;
|
||||||
const tileHeight = orientation === 'horizontal' ? 30 : 60;
|
const tileHeight = orientation === 'horizontal' ? 30 : 60;
|
||||||
|
|
||||||
const x = position.x + offset.x;
|
const x = position.x * zoom + offset.x;
|
||||||
const y = position.y + offset.y;
|
const y = position.y * zoom + offset.y;
|
||||||
|
const scaledWidth = tileWidth * zoom;
|
||||||
|
const scaledHeight = tileHeight * zoom;
|
||||||
|
|
||||||
// Draw tile background with shadow
|
// Draw tile background with shadow
|
||||||
ctx.shadowColor = 'rgba(0, 0, 0, 0.2)';
|
ctx.shadowColor = 'rgba(0, 0, 0, 0.2)';
|
||||||
ctx.shadowBlur = 5;
|
ctx.shadowBlur = 5 * zoom;
|
||||||
ctx.shadowOffsetX = 2;
|
ctx.shadowOffsetX = 2 * zoom;
|
||||||
ctx.shadowOffsetY = 2;
|
ctx.shadowOffsetY = 2 * zoom;
|
||||||
|
|
||||||
ctx.fillStyle = '#ffffff';
|
ctx.fillStyle = '#ffffff';
|
||||||
ctx.strokeStyle = '#1f2937';
|
ctx.strokeStyle = '#1f2937';
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2 * zoom;
|
||||||
|
|
||||||
const radius = 4;
|
const radius = 4 * zoom;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x + radius, y);
|
ctx.moveTo(x + radius, y);
|
||||||
ctx.lineTo(x + tileWidth - radius, y);
|
ctx.lineTo(x + scaledWidth - radius, y);
|
||||||
ctx.quadraticCurveTo(x + tileWidth, y, x + tileWidth, y + radius);
|
ctx.quadraticCurveTo(x + scaledWidth, y, x + scaledWidth, y + radius);
|
||||||
ctx.lineTo(x + tileWidth, y + tileHeight - radius);
|
ctx.lineTo(x + scaledWidth, y + scaledHeight - radius);
|
||||||
ctx.quadraticCurveTo(x + tileWidth, y + tileHeight, x + tileWidth - radius, y + tileHeight);
|
ctx.quadraticCurveTo(x + scaledWidth, y + scaledHeight, x + scaledWidth - radius, y + scaledHeight);
|
||||||
ctx.lineTo(x + radius, y + tileHeight);
|
ctx.lineTo(x + radius, y + scaledHeight);
|
||||||
ctx.quadraticCurveTo(x, y + tileHeight, x, y + tileHeight - radius);
|
ctx.quadraticCurveTo(x, y + scaledHeight, x, y + scaledHeight - radius);
|
||||||
ctx.lineTo(x, y + radius);
|
ctx.lineTo(x, y + radius);
|
||||||
ctx.quadraticCurveTo(x, y, x + radius, y);
|
ctx.quadraticCurveTo(x, y, x + radius, y);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
@@ -111,19 +153,19 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
|
|||||||
|
|
||||||
// Draw center divider
|
// Draw center divider
|
||||||
ctx.strokeStyle = '#6b7280';
|
ctx.strokeStyle = '#6b7280';
|
||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 1 * zoom;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
if (orientation === 'horizontal') {
|
if (orientation === 'horizontal') {
|
||||||
ctx.moveTo(x + tileWidth / 2, y);
|
ctx.moveTo(x + scaledWidth / 2, y);
|
||||||
ctx.lineTo(x + tileWidth / 2, y + tileHeight);
|
ctx.lineTo(x + scaledWidth / 2, y + scaledHeight);
|
||||||
} else {
|
} else {
|
||||||
ctx.moveTo(x, y + tileHeight / 2);
|
ctx.moveTo(x, y + scaledHeight / 2);
|
||||||
ctx.lineTo(x + tileWidth, y + tileHeight / 2);
|
ctx.lineTo(x + scaledWidth, y + scaledHeight / 2);
|
||||||
}
|
}
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
// Draw dots
|
// Draw dots
|
||||||
const dotRadius = 2.5;
|
const dotRadius = Math.max(2, 2.5 * zoom);
|
||||||
ctx.fillStyle = '#1f2937';
|
ctx.fillStyle = '#1f2937';
|
||||||
|
|
||||||
const drawDots = (value: number, dotX: number, dotY: number, size: number) => {
|
const drawDots = (value: number, dotX: number, dotY: number, size: number) => {
|
||||||
@@ -136,33 +178,33 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (orientation === 'horizontal') {
|
if (orientation === 'horizontal') {
|
||||||
const leftX = x + tileWidth / 4;
|
const leftX = x + scaledWidth / 4;
|
||||||
const rightX = x + (tileWidth * 3) / 4;
|
const rightX = x + (scaledWidth * 3) / 4;
|
||||||
const centerY = y + tileHeight / 2;
|
const centerY = y + scaledHeight / 2;
|
||||||
const dotAreaSize = tileWidth / 2 - 6;
|
const dotAreaSize = (scaledWidth / 2 - 6 * zoom);
|
||||||
|
|
||||||
drawDots(tile.left, leftX, centerY, dotAreaSize);
|
drawDots(tile.left, leftX, centerY, dotAreaSize);
|
||||||
drawDots(tile.right, rightX, centerY, dotAreaSize);
|
drawDots(tile.right, rightX, centerY, dotAreaSize);
|
||||||
} else {
|
} else {
|
||||||
const topY = y + tileHeight / 4;
|
const topY = y + scaledHeight / 4;
|
||||||
const bottomY = y + (tileHeight * 3) / 4;
|
const bottomY = y + (scaledHeight * 3) / 4;
|
||||||
const centerX = x + tileWidth / 2;
|
const centerX = x + scaledWidth / 2;
|
||||||
const dotAreaSize = tileHeight / 2 - 6;
|
const dotAreaSize = (scaledHeight / 2 - 6 * zoom);
|
||||||
|
|
||||||
drawDots(tile.left, centerX, topY, dotAreaSize);
|
drawDots(tile.left, centerX, topY, dotAreaSize);
|
||||||
drawDots(tile.right, centerX, bottomY, dotAreaSize);
|
drawDots(tile.right, centerX, bottomY, dotAreaSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw tile number for debugging (optional)
|
// Draw tile number for debugging (optional)
|
||||||
if (placedTiles.length < 20) {
|
if (placedTiles.length < 20 && zoom > 0.5) {
|
||||||
ctx.fillStyle = '#9ca3af';
|
ctx.fillStyle = '#9ca3af';
|
||||||
ctx.font = '10px sans-serif';
|
ctx.font = `${10 * zoom}px sans-serif`;
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.fillText(`#${index + 1}`, x + tileWidth / 2, y - 5);
|
ctx.fillText(`#${index + 1}`, x + scaledWidth / 2, y - 5 * zoom);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}, [placedTiles, offset, canvasSize.width, canvasSize.height]);
|
}, [placedTiles, offset, zoom, canvasSize.width, canvasSize.height]);
|
||||||
|
|
||||||
const handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
const handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
|
|||||||
Referencia en una nueva incidencia
Block a user