Commit ebe4288e authored by Smirnov Oleg's avatar Smirnov Oleg

pretty

parent be00b7bf
Pipeline #167 failed with stages
......@@ -141,7 +141,7 @@ export default function Home() {
)}
{currentPlayer && (
<div className="fixed top-4 left-4 bg-gray-800 rounded p-2">
<div className="fixed top-4 left-4 bg-gray-800 rounded p-2 z-50">
<PlayerInfo
nickname={`${currentPlayer.nickname} (${currentPlayer.score})`}
isCurrentTurn={gameState.isYourTurn}
......@@ -151,7 +151,7 @@ export default function Home() {
</div>
)}
{opponent && (
<div className="fixed bottom-4 left-4 bg-gray-800 rounded p-2">
<div className="fixed bottom-4 left-4 bg-gray-800 rounded p-2 z-50">
<PlayerInfo
nickname={`${opponent.nickname} (${opponent.score})`}
isCurrentTurn={!gameState.isYourTurn}
......@@ -161,14 +161,12 @@ export default function Home() {
</div>
)}
<div className="pt-20 pb-20">
<div className="h-screen">
{gameState.status === 'waiting' ? (
<div className="text-center text-xl">
Waiting for opponent to join...
</div>
) : (
<>
<div className="max-w-screen-sm mx-auto">
<GameBoard
cells={gameState.cells}
onCellClick={handleMove}
......@@ -177,7 +175,8 @@ export default function Home() {
lastMove={gameState.lastMove}
turnTimeLeft={gameState.isYourTurn ? turnTimeLeft : undefined}
/>
</div>
)}
{gameState.winner && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-gray-800 p-8 rounded-lg text-center">
......@@ -199,8 +198,6 @@ export default function Home() {
</div>
</div>
)}
</>
)}
</div>
</div>
);
......
......@@ -22,6 +22,7 @@ const GameBoard: React.FC<GameBoardProps> = ({
const [viewportPosition, setViewportPosition] = useState({ x: 0, y: 0 });
const [isDragging, setIsDragging] = useState(false);
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
const [isAnimatingMove, setIsAnimatingMove] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
......@@ -30,20 +31,60 @@ const GameBoard: React.FC<GameBoardProps> = ({
useEffect(() => {
if (containerRef.current) {
const container = containerRef.current;
setViewportPosition({
x: viewportSize / 2,
y: viewportSize / 2
x: container.clientWidth / 2,
y: container.clientHeight / 2
});
}
}, []);
useEffect(() => {
if (lastMove && isAnimatingMove) {
const container = containerRef.current;
if (!container) return;
const targetX = container.clientWidth / 2 - lastMove.x * cellSize;
const targetY = container.clientHeight / 2 - lastMove.y * cellSize;
// Анимируем перемещение к последнему ходу
const startX = viewportPosition.x;
const startY = viewportPosition.y;
const startTime = performance.now();
const duration = 1000; // 1 секунда на анимацию
const animate = (currentTime: number) => {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Функция плавности (ease-in-out)
const easeProgress = progress < 0.5
? 4 * progress * progress * progress
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
setViewportPosition({
x: startX + (targetX - startX) * easeProgress,
y: startY + (targetY - startY) * easeProgress
});
if (progress < 1) {
requestAnimationFrame(animate);
} else {
setIsAnimatingMove(false);
}
};
requestAnimationFrame(animate);
}
}, [lastMove, isAnimatingMove, cellSize]);
const getCellValue = (x: number, y: number): CellValue => {
const key = `${x},${y}`;
return cells[key] || null;
};
const handleCellClick = (x: number, y: number) => {
if (!isDragging) {
if (!isDragging && !isAnimatingMove) {
const value = getCellValue(x, y);
if (!value && isYourTurn) {
onCellClick(x, y);
......@@ -52,6 +93,7 @@ const GameBoard: React.FC<GameBoardProps> = ({
};
const handleMouseDown = (e: React.MouseEvent) => {
if (isAnimatingMove) return;
setIsDragging(true);
setDragStart({
x: e.clientX - viewportPosition.x,
......@@ -60,7 +102,7 @@ const GameBoard: React.FC<GameBoardProps> = ({
};
const handleMouseMove = (e: React.MouseEvent) => {
if (isDragging) {
if (isDragging && !isAnimatingMove) {
setViewportPosition({
x: e.clientX - dragStart.x,
y: e.clientY - dragStart.y
......@@ -102,13 +144,15 @@ const GameBoard: React.FC<GameBoardProps> = ({
const value = getCellValue(x, y);
const key = `${x},${y}`;
const isHoverable = !value && isYourTurn;
const isLastMove = lastMove && lastMove.x === x && lastMove.y === y;
return (
<div
key={key}
onClick={() => handleCellClick(x, y)}
className={`absolute flex items-center justify-center w-[58px] h-[58px] border border-gray-700 bg-gray-800
transition-all duration-200 ${isHoverable ? 'hover:bg-gray-700 cursor-pointer' : ''}`}
transition-all duration-200 ${isHoverable ? 'hover:bg-gray-700 cursor-pointer' : ''}
${isLastMove ? 'ring-2 ring-yellow-500' : ''}`}
style={{
left: x * cellSize,
top: y * cellSize,
......@@ -116,7 +160,8 @@ const GameBoard: React.FC<GameBoardProps> = ({
}}
>
{value && (
<div className="transform transition-transform duration-200 scale-100">
<div className={`transform transition-transform duration-200 scale-100
${isLastMove ? 'animate-pulse' : ''}`}>
<ServerIcon state={getServerIconState(value)} />
</div>
)}
......@@ -130,11 +175,8 @@ const GameBoard: React.FC<GameBoardProps> = ({
};
const moveToLastMove = () => {
if (lastMove) {
setViewportPosition({
x: viewportSize / 2 - lastMove.x * cellSize,
y: viewportSize / 2 - lastMove.y * cellSize
});
if (lastMove && !isAnimatingMove) {
setIsAnimatingMove(true);
}
};
......@@ -142,12 +184,27 @@ const GameBoard: React.FC<GameBoardProps> = ({
<div className="relative">
<div
ref={containerRef}
className={`relative w-[800px] h-[800px] bg-gray-900 overflow-hidden mx-auto
className={`relative w-screen h-screen bg-gray-900 overflow-hidden mx-auto
${isDragging ? 'cursor-grabbing' : 'cursor-grab'}`}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
onTouchStart={(e) => {
const touch = e.touches[0];
handleMouseDown({
clientX: touch.clientX,
clientY: touch.clientY
} as React.MouseEvent);
}}
onTouchMove={(e) => {
const touch = e.touches[0];
handleMouseMove({
clientX: touch.clientX,
clientY: touch.clientY
} as React.MouseEvent);
}}
onTouchEnd={handleMouseUp}
>
<div
style={{
......@@ -159,13 +216,14 @@ const GameBoard: React.FC<GameBoardProps> = ({
</div>
</div>
<div className="absolute top-4 right-4 flex flex-col gap-2">
<div className="fixed top-4 right-4 flex flex-col gap-2 z-50">
{lastMove && (
<button
onClick={moveToLastMove}
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded transition-colors"
disabled={isAnimatingMove}
>
Показать последний ход
Последний ход
</button>
)}
{turnTimeLeft !== undefined && (
......
......@@ -9,7 +9,7 @@ interface PlayerInfoProps {
const PlayerInfo: React.FC<PlayerInfoProps> = ({ nickname, isCurrentTurn, isAttacker, position }) => {
return (
<div className={`fixed ${position === 'top' ? 'top-4' : 'bottom-4'} left-4 flex items-center gap-4 bg-gray-800 rounded-lg p-4 text-white`}>
<div className={`fixed ${position === 'top' ? 'top-0' : 'bottom-0'} left-0 flex items-center gap-4 bg-gray-800 rounded-lg p-4 text-white`}>
<div className={`w-12 h-12 rounded-full flex items-center justify-center ${
isCurrentTurn ? (isAttacker ? 'bg-red-500' : 'bg-green-500') : 'bg-gray-600'
}`}>
......@@ -18,7 +18,7 @@ const PlayerInfo: React.FC<PlayerInfoProps> = ({ nickname, isCurrentTurn, isAtta
<div>
<div className="font-medium">{nickname}</div>
<div className={`text-sm ${isCurrentTurn ? 'text-yellow-400' : 'text-gray-400'}`}>
{isCurrentTurn ? 'Your turn' : 'Waiting...'}
{isCurrentTurn ? 'Твой ход' : 'Ожидание...'}
</div>
</div>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment