teletilt #3
18
app/AppContext.tsx
Normal file
18
app/AppContext.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { createContext, useContext, useState, ReactNode } from 'react';
|
||||||
|
import type { AppContext, Tilt } from '@/types';
|
||||||
|
|
||||||
|
const AppContext = createContext<AppContext | undefined>(undefined);
|
||||||
|
|
||||||
|
export function AppProvider({ children }: { children: ReactNode }) {
|
||||||
|
const [tilts, setTilts] = useState<Tilt[]>([]);
|
||||||
|
|
||||||
|
return <AppContext.Provider value={{ tilts, setTilts }}>{children}</AppContext.Provider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAppContext(): AppContext {
|
||||||
|
const context = useContext(AppContext);
|
||||||
|
if (!context) throw new Error('useAppContext must be used within AppProvider');
|
||||||
|
return context;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import { Pirata_One, Eagle_Lake, Cinzel_Decorative } from 'next/font/google';
|
import { Pirata_One, Eagle_Lake, Cinzel_Decorative } from 'next/font/google';
|
||||||
|
import { AppProvider } from '@/app/AppContext';
|
||||||
import './globals.css';
|
import './globals.css';
|
||||||
|
|
||||||
const pirataOne = Pirata_One({
|
const pirataOne = Pirata_One({
|
||||||
@@ -40,7 +41,9 @@ export default function RootLayout({
|
|||||||
lang="en"
|
lang="en"
|
||||||
className={`${pirataOne.variable} ${eagleLake.variable} ${cinzel.variable} antialiased`}
|
className={`${pirataOne.variable} ${eagleLake.variable} ${cinzel.variable} antialiased`}
|
||||||
>
|
>
|
||||||
<body className={`${eagleLake.className} antialiased`}>{children}</body>
|
<body className={`${eagleLake.className} antialiased`}>
|
||||||
|
<AppProvider>{children}</AppProvider>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,10 +60,11 @@ export default function Card({
|
|||||||
<ToolTip content={tooltip || getTooltip()}>
|
<ToolTip content={tooltip || getTooltip()}>
|
||||||
<TiltCard
|
<TiltCard
|
||||||
className={`h-[21vh] w-[15vh] relative perspective transition-transform duration-200 z-0 hover:z-10 hover:scale-150 ${dm ? 'cursor-pointer' : ''} `}
|
className={`h-[21vh] w-[15vh] relative perspective transition-transform duration-200 z-0 hover:z-10 hover:scale-150 ${dm ? 'cursor-pointer' : ''} `}
|
||||||
onClick={handleClick}
|
cardID={position.id}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`absolute inset-0 transition-transform duration-500 transform-style-preserve-3d ${flipped ? 'rotate-y-180' : ''}`}
|
className={`absolute inset-0 transition-transform duration-500 transform-style-preserve-3d ${flipped ? 'rotate-y-180' : ''}`}
|
||||||
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 group backface-hidden">
|
<div className="absolute inset-0 group backface-hidden">
|
||||||
{dm && (
|
{dm && (
|
||||||
|
|||||||
@@ -1,18 +1,39 @@
|
|||||||
import { useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { useAppContext } from '@/app/AppContext';
|
||||||
|
|
||||||
export default function TiltCard({
|
export default function TiltCard({
|
||||||
children,
|
children,
|
||||||
|
cardID,
|
||||||
className = '',
|
className = '',
|
||||||
onClick = () => {},
|
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
cardID: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick: (event: React.MouseEvent) => void;
|
|
||||||
}) {
|
}) {
|
||||||
const cardRef = useRef<HTMLDivElement>(null);
|
const cardRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { tilts, setTilts } = useAppContext();
|
||||||
|
|
||||||
|
const card = cardRef.current;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!card) return;
|
||||||
|
|
||||||
|
const tilt = tilts.find((tilt) => tilt.cardID === cardID);
|
||||||
|
if (!tilt) {
|
||||||
|
card.style.transform = `rotateX(0deg) rotateY(0deg)`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rotateX, rotateY } = tilt;
|
||||||
|
|
||||||
|
if (rotateX || rotateY) {
|
||||||
|
card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
|
||||||
|
} else {
|
||||||
|
card.style.transform = `rotateX(0deg) rotateY(0deg)`;
|
||||||
|
}
|
||||||
|
}, [tilts]);
|
||||||
|
|
||||||
const handleMouseMove = (e: React.MouseEvent) => {
|
const handleMouseMove = (e: React.MouseEvent) => {
|
||||||
const card = cardRef.current;
|
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
|
||||||
const rect = card.getBoundingClientRect();
|
const rect = card.getBoundingClientRect();
|
||||||
@@ -24,22 +45,17 @@ export default function TiltCard({
|
|||||||
const rotateX = ((y - centerY) / centerY) * -20;
|
const rotateX = ((y - centerY) / centerY) * -20;
|
||||||
const rotateY = ((x - centerX) / centerX) * 20;
|
const rotateY = ((x - centerX) / centerX) * 20;
|
||||||
|
|
||||||
card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
|
setTilts([...tilts.filter((tilt) => tilt.cardID !== cardID), { cardID, rotateX, rotateY }]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
const card = cardRef.current;
|
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
card.style.transform = `rotateX(0deg) rotateY(0deg)`;
|
|
||||||
|
setTilts(tilts.filter((tilt) => tilt.cardID !== cardID));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={`${className}`} onMouseMove={handleMouseMove} onMouseLeave={handleMouseLeave}>
|
||||||
className={`${className}`}
|
|
||||||
onMouseMove={handleMouseMove}
|
|
||||||
onMouseLeave={handleMouseLeave}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
<div ref={cardRef} className={`h-full w-full transition-transform duration-0`}>
|
<div ref={cardRef} className={`h-full w-full transition-transform duration-0`}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
12
server.ts
12
server.ts
@@ -4,7 +4,7 @@ import { Server as SocketIOServer, type Socket } from 'socket.io';
|
|||||||
|
|
||||||
import GameStore from '@/lib/GameStore';
|
import GameStore from '@/lib/GameStore';
|
||||||
import omit from '@/tools/omit';
|
import omit from '@/tools/omit';
|
||||||
import type { ClientUpdate, GameUpdate } from '@/types';
|
import type { ClientUpdate, GameUpdate, Tilt } from '@/types';
|
||||||
|
|
||||||
const dev = process.env.NODE_ENV !== 'production';
|
const dev = process.env.NODE_ENV !== 'production';
|
||||||
const hostname = '0.0.0.0';
|
const hostname = '0.0.0.0';
|
||||||
@@ -119,6 +119,16 @@ app.prepare().then(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('tilt', ({ gameID, tilt }: { gameID: string; tilt: Tilt }) => {
|
||||||
|
try {
|
||||||
|
const gameState = gameStore.getGame(gameID);
|
||||||
|
broadcast('tilt', gameState);
|
||||||
|
} catch (e) {
|
||||||
|
const error = e instanceof Error ? e.message : e;
|
||||||
|
console.error(Date.now(), 'Error[tilt]', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
try {
|
try {
|
||||||
const game = gameStore.playerExit(socket.id);
|
const game = gameStore.playerExit(socket.id);
|
||||||
|
|||||||
@@ -103,3 +103,14 @@ export interface Layout {
|
|||||||
name: string;
|
name: string;
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Tilt {
|
||||||
|
cardID: string;
|
||||||
|
rotateX: number;
|
||||||
|
rotateY: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppContext {
|
||||||
|
tilts: Tilt[];
|
||||||
|
setTilts: (tilts: Tilt[]) => void;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user