From c6e316a1f8537eedd21e99359c3fecb56c9dbb87 Mon Sep 17 00:00:00 2001 From: Gavin McDonald Date: Fri, 4 Jul 2025 13:59:57 -0400 Subject: [PATCH] simplify tilt calculations --- app/AppContext.tsx | 21 +++++++++-------- components/Sheen.tsx | 40 ++++++-------------------------- components/TiltCard.tsx | 51 +++++++++-------------------------------- tools/index.ts | 4 ++++ tools/log.tsx | 19 +++++++++++++++ tools/reduceTilts.ts | 34 +++++++++++++++++++++++++++ tools/throttle.ts | 2 +- tools/validTilt.ts | 4 ++++ 8 files changed, 91 insertions(+), 84 deletions(-) create mode 100644 tools/index.ts create mode 100644 tools/log.tsx create mode 100644 tools/reduceTilts.ts create mode 100644 tools/validTilt.ts diff --git a/app/AppContext.tsx b/app/AppContext.tsx index 664186e..f530a13 100644 --- a/app/AppContext.tsx +++ b/app/AppContext.tsx @@ -2,6 +2,7 @@ import { createContext, useContext, useEffect, useState } from 'react'; import useSocket from '@/hooks/useSocket'; +import { reduceTilts } from '@/tools'; import { GAME_START, LOCAL_DEFAULTS } from '@/constants'; import type { Dispatch, ReactNode, SetStateAction } from 'react'; @@ -15,7 +16,7 @@ export interface AppContext { noGame: boolean; selectCardIndex: number; settings: Settings; - tilt: Tilt[]; + tilts: Tilt[]; emitFlip: (cardIndex: number) => void; emitSettings: (gameData: GameUpdate) => void; emitRedraw: (cardIndex: number) => void; @@ -23,7 +24,7 @@ export interface AppContext { setGameID: (gameID: string) => void; setLocalSettings: Dispatch>; setSelectCardIndex: (cardIndex: number) => void; - setTilt: (tilt: Tilt[]) => void; + setLocalTilt: (tilt: Tilt[]) => void; } export function AppProvider({ children }: { children: ReactNode }) { @@ -32,7 +33,7 @@ export function AppProvider({ children }: { children: ReactNode }) { const [gameID, setGameID] = useState(''); const [noGame, setNoGame] = useState(false); const [selectCardIndex, setSelectCardIndex] = useState(-1); - const [tilt, setTilt] = useState([]); + const [localTilt, setLocalTilt] = useState([]); const { emitFlip, emitRedraw, emitSelect, emitSettings, emitTilt } = useSocket({ gameID, @@ -42,17 +43,17 @@ export function AppProvider({ children }: { children: ReactNode }) { useEffect(() => { if (localSettings.remoteTilt) { - const cardIndex = tilt.findIndex((tilt) => !!tilt); + const cardIndex = localTilt.findIndex((tilt) => !!tilt); - if (tilt[cardIndex]) { - emitTilt(cardIndex, tilt[cardIndex]); + if (localTilt[cardIndex]) { + emitTilt(cardIndex, localTilt[cardIndex]); } else { // cardIndex does not matter // all tilts for this user will be cleared - emitTilt(0, { rotateX: 0, rotateY: 0 }); + emitTilt(0, { percentX: -1, percentY: -1, rotateX: 0, rotateY: 0 }); } } - }, [tilt, localSettings]); + }, [localTilt, localSettings]); const handleSelect = (cardID: string) => { setSelectCardIndex(-1); @@ -69,7 +70,7 @@ export function AppProvider({ children }: { children: ReactNode }) { noGame, selectCardIndex, settings: { ...gameData.settings, ...localSettings }, - tilt, + tilts: reduceTilts(gameData, localTilt), emitFlip, emitSettings, emitRedraw, @@ -77,7 +78,7 @@ export function AppProvider({ children }: { children: ReactNode }) { setGameID, setLocalSettings, setSelectCardIndex, - setTilt, + setLocalTilt, }; return {children}; diff --git a/components/Sheen.tsx b/components/Sheen.tsx index 8afbd2e..ab43d52 100644 --- a/components/Sheen.tsx +++ b/components/Sheen.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { useAppContext } from '@/app/AppContext'; +import { validTilt } from '@/tools'; const tiltSheen = (sheen: HTMLDivElement, x: number, y: number) => { const rect = sheen.getBoundingClientRect(); @@ -20,48 +21,21 @@ const tiltSheen = (sheen: HTMLDivElement, x: number, y: number) => { export default function Sheen({ cardIndex, className }: { cardIndex: number; className?: string }) { const sheenRef = useRef(null); const [untilt, setUntilt] = useState(false); - const { - gameData, - settings: { tilt, remoteTilt }, - tilt: localTilts, - } = useAppContext(); + const { tilts } = useAppContext(); useEffect(() => { const sheen = sheenRef.current; if (!sheen) return; - if (tilt) { - const percentX = localTilts[cardIndex]?.percentX || -1; - const percentY = localTilts[cardIndex]?.percentY || -1; + const tilt = tilts[cardIndex]; - const percentages = remoteTilt - ? [...gameData.tilts[cardIndex], { percentX, percentY }] - : [{ percentX, percentY }]; - - const { totalX, totalY, count } = percentages - .filter(({ percentX, percentY }) => percentX >= 0 && percentY >= 0) - .reduce( - ({ totalX, totalY, count }, { percentX, percentY }) => ({ - totalX: totalX + percentX, - totalY: totalY + percentY, - count: ++count, - }), - { totalX: 0, totalY: 0, count: 0 }, - ); - - if (count && totalX >= 0 && totalY >= 0) { - const x = totalX / count; - const y = totalY / count; - - tiltSheen(sheen, x, y); - setUntilt(false); - } else { - setUntilt(true); - } + if (validTilt(tilt)) { + setUntilt(false); + tiltSheen(sheen, tilt.percentX, tilt.percentY); } else { setUntilt(true); } - }, [tilt, localTilts, gameData]); + }, [tilts]); useEffect(() => { const sheen = sheenRef.current; diff --git a/components/TiltCard.tsx b/components/TiltCard.tsx index cfa7762..2cb305e 100644 --- a/components/TiltCard.tsx +++ b/components/TiltCard.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { useAppContext } from '@/app/AppContext'; -import throttle from '@/tools/throttle'; +import { throttle, validTilt } from '@/tools'; import { thirtyFPS } from '@/constants/time'; import type { Tilt } from '@/types'; @@ -18,50 +18,21 @@ export default function TiltCard({ }) { const cardRef = useRef(null); const [untilt, setUntilt] = useState(false); - const { - gameData, - settings: { tilt, remoteTilt }, - setTilt, - tilt: localTilts, - } = useAppContext(); + const { settings, tilts, setLocalTilt } = useAppContext(); useEffect(() => { const card = cardRef.current; if (!card) return; - if (tilt) { - const rotateX = localTilts[cardIndex]?.rotateX || 0; - const rotateY = localTilts[cardIndex]?.rotateY || 0; + const tilt = tilts[cardIndex]; - const tilts = remoteTilt - ? [...gameData.tilts[cardIndex], { rotateX, rotateY }] - : [{ rotateX, rotateY }]; - - const { totalX, totalY, count } = tilts - .filter(({ rotateX, rotateY }) => !!rotateX && !!rotateY) - .reduce( - ({ totalX, totalY, count }, { rotateX, rotateY }) => ({ - totalX: totalX + rotateX, - totalY: totalY + rotateY, - count: ++count, - }), - { totalX: 0, totalY: 0, count: 0 }, - ); - - if (count && (totalX || totalY)) { - setUntilt(false); - - const x = totalX / count; - const y = totalY / count; - - card.style.transform = `rotateX(${x}deg) rotateY(${y}deg)`; - } else { - setUntilt(true); - } - } else if (card.style.transform !== ZERO_ROTATION) { + if (validTilt(tilt)) { + setUntilt(false); + card.style.transform = `rotateX(${tilt.rotateX}deg) rotateY(${tilt.rotateY}deg)`; + } else { setUntilt(true); } - }, [tilt, localTilts, gameData]); + }, [tilts]); useEffect(() => { const card = cardRef.current; @@ -93,17 +64,17 @@ export default function TiltCard({ rotateY, }; - setTilt(newTilt); + setLocalTilt(newTilt); }, thirtyFPS); const handleMouseLeave = () => { - setTilt([]); + setLocalTilt([]); }; return (
any} - A function that logs its arguments and returns the first one. + * + * @example + * const result = [1, 2, 3] + * .map((n) => n * 2) + * .map(log('doubled:')) + * .filter((n) => n > 2); + */ +export const log = + (prefix: string = ''): ((value: any, ...rest: any[]) => any) => + (...args: any[]) => { + console.log(prefix, ...args); + return args[0]; + }; diff --git a/tools/reduceTilts.ts b/tools/reduceTilts.ts new file mode 100644 index 0000000..1167997 --- /dev/null +++ b/tools/reduceTilts.ts @@ -0,0 +1,34 @@ +import { log, validTilt } from '@/tools'; +import { GameUpdate, Tilt } from '@/types'; + +const combineTilts = (tilts: Tilt[]) => + tilts.reduce( + ({ pX, pY, rX, rY, count }, { percentX, percentY, rotateX, rotateY }) => ({ + pX: pX + percentX, + pY: pY + percentY, + rX: rX + rotateX, + rY: rY + rotateY, + count: count + 1, + }), + { pX: 0, pY: 0, rX: 0, rY: 0, count: 0 }, + ); + +export function reduceTilts(gameData: GameUpdate, localTilt: Tilt[]): Tilt[] { + const remoteTilts = gameData.tilts; + const tiltEnabled = gameData.settings.tilt; + const remoteTiltEnabled = gameData.settings.remoteTilt; + + if (!tiltEnabled) return []; + if (!remoteTiltEnabled) return Array.from({ length: 5 }, (_, i) => localTilt[i]); + + return Array.from({ length: 5 }, (_, i) => (localTilt[i] ? [localTilt[i]] : [])) + .map((cardTilts, cardIndex) => [...remoteTilts[cardIndex], ...cardTilts]) + .map((cardTilts) => cardTilts.filter(validTilt)) + .map(combineTilts) + .map(({ pX, pY, rX, rY, count }) => ({ + percentX: count ? pX / count : -1, + percentY: count ? pY / count : -1, + rotateX: count ? rX / count : 0, + rotateY: count ? rY / count : 0, + })); +} diff --git a/tools/throttle.ts b/tools/throttle.ts index db1f15a..64b40c4 100644 --- a/tools/throttle.ts +++ b/tools/throttle.ts @@ -1,4 +1,4 @@ -export default function throttle(func: Function, threshold: number) { +export function throttle(func: Function, threshold: number) { let lastCall = 0; return (...args: any[]) => { diff --git a/tools/validTilt.ts b/tools/validTilt.ts new file mode 100644 index 0000000..cae0758 --- /dev/null +++ b/tools/validTilt.ts @@ -0,0 +1,4 @@ +import { Tilt } from '@/types'; + +export const validTilt = ({ percentX, percentY, rotateX, rotateY }: Tilt) => + percentX >= 0 && percentY >= 0 && !!rotateX && !!rotateY;