diff --git a/src/components/FloatingMessage.tsx b/src/components/FloatingMessage.tsx deleted file mode 100644 index 4891cc2..0000000 --- a/src/components/FloatingMessage.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useEffect, useState } from 'react' - -interface FloatingMessageProps { - message: string - startPosition: { x: number; y: number } - onComplete: () => void -} - -const FloatingMessage: React.FC = ({ - message, - startPosition, - onComplete, -}) => { - const [position, setPosition] = useState({ y: startPosition.y }) - const [opacity, setOpacity] = useState(1) - - useEffect(() => { - const startTime = Date.now() - const duration = 1000 // 1 second animation - - const animate = () => { - const elapsed = Date.now() - startTime - const progress = Math.min(elapsed / duration, 1) - - setPosition({ y: startPosition.y - progress * 50 }) - setOpacity(1 - progress) - - if (progress < 1) { - requestAnimationFrame(animate) - } else { - onComplete() - } - } - - const animationFrame = requestAnimationFrame(animate) - - return () => { - cancelAnimationFrame(animationFrame) - } - }, [startPosition, onComplete]) - - return ( -
- {message} -
- ) -} - -export default FloatingMessage diff --git a/src/components/FloatingMessages.tsx b/src/components/FloatingMessages.tsx new file mode 100644 index 0000000..4972780 --- /dev/null +++ b/src/components/FloatingMessages.tsx @@ -0,0 +1,74 @@ +import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react' + +interface Message { + id: string + text: string + position: { x: number; y: number } +} + +export interface FloatingMessagesHandle { + addMessage: (text: string, position: { x: number; y: number }) => void +} + +const FloatingMessages = forwardRef((_, ref) => { + const [messages, setMessages] = useState([]) + + useImperativeHandle(ref, () => ({ + addMessage: (text: string, position: { x: number; y: number }) => { + const newMessage: Message = { + id: Math.random().toString(36).substring(2), + text, + position, + } + setMessages(prev => [...prev, newMessage]) + } + })) + + const handleAnimationEnd = (messageId: string) => { + setMessages(prev => prev.filter(msg => msg.id !== messageId)) + } + + return ( + <> + + {messages.map(message => ( +
handleAnimationEnd(message.id)} + > + {message.text} +
+ ))} + + ) +}) + +FloatingMessages.displayName = 'FloatingMessages' + +export default FloatingMessages diff --git a/src/components/Market.tsx b/src/components/Market.tsx index 60a4cd2..5b0231a 100644 --- a/src/components/Market.tsx +++ b/src/components/Market.tsx @@ -1,7 +1,7 @@ -import React, { useState } from 'react' +import React, { useRef } from 'react' import { useGameStore } from '../store/useGameStore' import { MARKET_ITEMS } from '../constants' -import FloatingMessage from './FloatingMessage' +import FloatingMessage, { FloatingMessagesHandle } from './FloatingMessages' import { styled } from '@linaria/react' const MarketContainer = styled.div` @@ -71,17 +71,9 @@ const ActionButton = styled.button<{ disabled?: boolean }>` border: none; ` -interface FloatingMessageData { - id: string - message: string - position: { x: number; y: number } -} - const MarketComponent: React.FC = () => { const { inventory, cash, buyItem, sellItem } = useGameStore() - const [floatingMessages, setFloatingMessages] = useState< - FloatingMessageData[] - >([]) + const floatingMessagesRef = useRef(null) const handleBuy = (itemId: string, e: React.MouseEvent) => { const item = MARKET_ITEMS[itemId] @@ -92,14 +84,10 @@ const MarketComponent: React.FC = () => { const rect = (e.target as HTMLElement).getBoundingClientRect() const position = { x: rect.left + rect.width / 2, y: rect.top } - setFloatingMessages([ - ...floatingMessages, - { - id: `buy-${itemId}-${Date.now()}`, - message: `+1 ${item.emoji} ${item.name}`, - position, - }, - ]) + floatingMessagesRef.current?.addMessage( + `+1 ${item.emoji} ${item.name}`, + position + ) } const handleSell = (itemId: string, e: React.MouseEvent) => { @@ -117,18 +105,10 @@ const MarketComponent: React.FC = () => { const rect = (e.target as HTMLElement).getBoundingClientRect() const position = { x: rect.left + rect.width / 2, y: rect.top } - setFloatingMessages([ - ...floatingMessages, - { - id: `sell-${itemId}-${Date.now()}`, - message: `-1 ${item.emoji} ${item.name}`, - position, - }, - ]) - } - - const removeFloatingMessage = (messageId: string) => { - setFloatingMessages(floatingMessages.filter((msg) => msg.id !== messageId)) + floatingMessagesRef.current?.addMessage( + `-1 ${item.emoji} ${item.name}`, + position + ) } const sellableItems = Object.entries(MARKET_ITEMS) @@ -216,14 +196,7 @@ const MarketComponent: React.FC = () => { - {floatingMessages.map((msg) => ( - removeFloatingMessage(msg.id)} - /> - ))} + ) } diff --git a/src/constants/index.ts b/src/constants/index.ts index f35c2a3..9014145 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1,7 +1,7 @@ import { CropDefinitions } from '../types' export const INITIAL_CASH = 50 -export const INITIAL_FIELD_SIZE = 4 +export const INITIAL_FIELD_SIZE = 3 export const COOLDOWN_DURATION = 2000 // 2 seconds in milliseconds export const TICK_INTERVAL = 12000 // 12 seconds in milliseconds export const GAME_SPEEDS = {