diff --git a/frontend/src/canvas/CanvasContainer.css b/frontend/src/canvas/CanvasContainer.css index c248840c..9aac1908 100644 --- a/frontend/src/canvas/CanvasContainer.css +++ b/frontend/src/canvas/CanvasContainer.css @@ -6,15 +6,17 @@ } .CanvasContainer__grid { - /* background: rgba(0, 0, 0, 0.2); */ padding: 20px; border-radius: 8px; } +.CanvasContainer__grid > div { + transition: all 0.3s ease-in-out; +} + .CanvasContainer__grid > div:hover { - opacity: 1; - transform: scale(1.02); - transition: all 0.2s ease; + opacity: 1 !important; + transform: translate(-50%, -50%) scale(1.02) !important; z-index: 1; } diff --git a/frontend/src/canvas/CanvasContainer.js b/frontend/src/canvas/CanvasContainer.js index 4274c917..42997db6 100644 --- a/frontend/src/canvas/CanvasContainer.js +++ b/frontend/src/canvas/CanvasContainer.js @@ -195,19 +195,19 @@ const CanvasContainer = (props) => { }; useEffect(() => { - canvasContainerRef.current.addEventListener('wheel', zoom); - canvasContainerRef.current.addEventListener('touchstart', handleTouchStart); - canvasContainerRef.current.addEventListener('touchmove', handleTouchMove); + const currentRef = canvasContainerRef.current; + if (!currentRef) return; + + currentRef.addEventListener('wheel', zoom); + currentRef.addEventListener('touchstart', handleTouchStart); + currentRef.addEventListener('touchmove', handleTouchMove); + return () => { - canvasContainerRef.current.removeEventListener('wheel', zoom); - canvasContainerRef.current.removeEventListener( - 'touchstart', - handleTouchStart - ); - canvasContainerRef.current.removeEventListener( - 'touchmove', - handleTouchMove - ); + if (currentRef) { + currentRef.removeEventListener('wheel', zoom); + currentRef.removeEventListener('touchstart', handleTouchStart); + currentRef.removeEventListener('touchmove', handleTouchMove); + } }; }, [canvasScale, canvasX, canvasY, touchInitialDistance]); @@ -563,6 +563,42 @@ const CanvasContainer = (props) => { // Add state for hover const [hoveredWorld, setHoveredWorld] = useState(null); + const getGridPositions = (totalWorlds) => { + if (totalWorlds <= 0) return []; + + // Calculate the required grid size + // Start with 1x1, then 3x3, 5x5, 7x7, etc. + let gridSize = 1; + while (gridSize * gridSize < totalWorlds) { + gridSize += 2; + } + + const positions = []; + const center = Math.floor(gridSize / 2); + + // Generate positions in a spiral pattern from center + for (let layer = 0; layer <= center; layer++) { + if (layer === 0) { + // Center position + positions.push([0, 0]); + continue; + } + + // Generate positions for current layer (ring) + for (let i = -layer; i <= layer; i++) { + for (let j = -layer; j <= layer; j++) { + // Skip if not on the edge of the current layer + if (Math.abs(i) !== layer && Math.abs(j) !== layer) continue; + positions.push([i, j]); + } + } + } + + return positions.slice(0, totalWorlds); + }; + + console.log('all worlds: ', props.worlds); + return (
{
{ transformOrigin: 'center' }} > - {props.worlds?.map((world) => { - const isCenter = world.worldId === props.openedWorldId; - - return ( -
setHoveredWorld(world.worldId)} - onMouseLeave={() => setHoveredWorld(null)} - onClick={() => { - if (!isCenter) { - props.setOpenedWorldId(world.worldId); - // Update URL - window.history.pushState({}, '', `/worlds/${world.worldId}`); - } - }} - > - {}} - openedWorldId={world.worldId} - showTitle={hoveredWorld === world.worldId} - worldName={world.name} - /> -
- ); - })} + {(() => { + if (!props.worlds || !props.worlds.length) return null; + + const positions = getGridPositions(props.worlds.length); + const centerIndex = 0; // Center position is always first + + return props.worlds.map((world, index) => { + if (!world) return null; + + const isCenter = world.worldId === props.openedWorldId; + let position = positions[index]; + + // Swap position with center if this is the selected world + if (isCenter && index !== centerIndex) { + position = positions[centerIndex]; + } else if (!isCenter && index === centerIndex) { + const selectedIndex = props.worlds.findIndex( + (w) => w?.worldId === props.openedWorldId + ); + if (selectedIndex !== -1 && selectedIndex < positions.length) { + position = positions[selectedIndex]; + } + } + + return ( +
setHoveredWorld(world.worldId)} + onMouseLeave={() => setHoveredWorld(null)} + onClick={() => { + if (!isCenter) { + props.setOpenedWorldId(world.worldId); + window.history.pushState( + {}, + '', + `/worlds/${world.worldId}` + ); + } + }} + > + {}} + openedWorldId={world.worldId} + showTitle={hoveredWorld === world.worldId} + worldName={world.name} + /> +
+ ); + }); + })()}
);