Animate points
This commit is contained in:
parent
c38a3e1fdf
commit
156c210dea
3 changed files with 45 additions and 8 deletions
8
App.tsx
8
App.tsx
|
|
@ -53,7 +53,9 @@ function App() {
|
||||||
lastPointsEarned,
|
lastPointsEarned,
|
||||||
nextQuestion,
|
nextQuestion,
|
||||||
currentCorrectShape,
|
currentCorrectShape,
|
||||||
selectedOption
|
selectedOption,
|
||||||
|
currentPlayerScore,
|
||||||
|
currentStreak
|
||||||
} = useGame();
|
} = useGame();
|
||||||
|
|
||||||
const currentQ = quiz?.questions[currentQuestionIndex];
|
const currentQ = quiz?.questions[currentQuestionIndex];
|
||||||
|
|
@ -123,8 +125,8 @@ function App() {
|
||||||
<RevealScreen
|
<RevealScreen
|
||||||
isCorrect={lastPointsEarned !== null && lastPointsEarned > 0}
|
isCorrect={lastPointsEarned !== null && lastPointsEarned > 0}
|
||||||
pointsEarned={lastPointsEarned || 0}
|
pointsEarned={lastPointsEarned || 0}
|
||||||
newScore={0}
|
newScore={currentPlayerScore}
|
||||||
streak={0}
|
streak={currentStreak}
|
||||||
correctOption={correctOpt}
|
correctOption={correctOpt}
|
||||||
selectedOption={selectedOption}
|
selectedOption={selectedOption}
|
||||||
role={role}
|
role={role}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,31 @@
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion, useSpring, useTransform } from 'framer-motion';
|
||||||
import { Check, X, Flame, Trophy } from 'lucide-react';
|
import { Check, X, Flame, Trophy } from 'lucide-react';
|
||||||
import { AnswerOption, Player, GameRole } from '../types';
|
import { AnswerOption, Player, GameRole } from '../types';
|
||||||
import { SHAPES, COLORS } from '../constants';
|
import { SHAPES, COLORS } from '../constants';
|
||||||
import confetti from 'canvas-confetti';
|
import confetti from 'canvas-confetti';
|
||||||
|
|
||||||
|
const AnimatedCounter: React.FC<{ from: number; to: number }> = ({ from, to }) => {
|
||||||
|
const springValue = useSpring(from, {
|
||||||
|
stiffness: 50,
|
||||||
|
damping: 20,
|
||||||
|
});
|
||||||
|
const [displayValue, setDisplayValue] = useState(from);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
springValue.set(to);
|
||||||
|
}, [to, springValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribe = springValue.on('change', (latest) => {
|
||||||
|
setDisplayValue(Math.round(Number(latest)));
|
||||||
|
});
|
||||||
|
return unsubscribe;
|
||||||
|
}, [springValue]);
|
||||||
|
|
||||||
|
return <span>{displayValue}</span>;
|
||||||
|
};
|
||||||
|
|
||||||
interface RevealScreenProps {
|
interface RevealScreenProps {
|
||||||
isCorrect: boolean;
|
isCorrect: boolean;
|
||||||
pointsEarned: number;
|
pointsEarned: number;
|
||||||
|
|
@ -148,7 +169,7 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="mt-8 bg-black/20 px-6 py-2 rounded-xl text-xl font-bold">
|
<div className="mt-8 bg-black/20 px-6 py-2 rounded-xl text-xl font-bold">
|
||||||
Total Score: {newScore}
|
Total Score: <AnimatedCounter from={newScore - (pointsEarned || 0)} to={newScore} />
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ export const useGame = () => {
|
||||||
const [currentCorrectShape, setCurrentCorrectShape] = useState<string | null>(null);
|
const [currentCorrectShape, setCurrentCorrectShape] = useState<string | null>(null);
|
||||||
const [lastPointsEarned, setLastPointsEarned] = useState<number | null>(null);
|
const [lastPointsEarned, setLastPointsEarned] = useState<number | null>(null);
|
||||||
const [selectedOption, setSelectedOption] = useState<AnswerOption | null>(null);
|
const [selectedOption, setSelectedOption] = useState<AnswerOption | null>(null);
|
||||||
|
const [currentPlayerScore, setCurrentPlayerScore] = useState(0);
|
||||||
|
const [currentStreak, setCurrentStreak] = useState(0);
|
||||||
|
|
||||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
const peerRef = useRef<Peer | null>(null);
|
const peerRef = useRef<Peer | null>(null);
|
||||||
|
|
@ -302,6 +304,12 @@ export const useGame = () => {
|
||||||
|
|
||||||
if (data.type === 'RESULT') {
|
if (data.type === 'RESULT') {
|
||||||
setLastPointsEarned(data.payload.scoreAdded);
|
setLastPointsEarned(data.payload.scoreAdded);
|
||||||
|
setCurrentPlayerScore(data.payload.newScore);
|
||||||
|
if (data.payload.isCorrect) {
|
||||||
|
setCurrentStreak(prev => prev + 1);
|
||||||
|
} else {
|
||||||
|
setCurrentStreak(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type === 'TIME_UP') {
|
if (data.type === 'TIME_UP') {
|
||||||
|
|
@ -329,9 +337,15 @@ export const useGame = () => {
|
||||||
const points = isCorrect ? Math.round(POINTS_PER_QUESTION * (timeLeftRef.current / QUESTION_TIME)) : 0;
|
const points = isCorrect ? Math.round(POINTS_PER_QUESTION * (timeLeftRef.current / QUESTION_TIME)) : 0;
|
||||||
setLastPointsEarned(points);
|
setLastPointsEarned(points);
|
||||||
|
|
||||||
|
const hostPlayer = playersRef.current.find(p => p.id === 'host');
|
||||||
|
const newScore = (hostPlayer?.score || 0) + points;
|
||||||
|
const newStreak = isCorrect ? (hostPlayer?.streak || 0) + 1 : 0;
|
||||||
|
setCurrentPlayerScore(newScore);
|
||||||
|
setCurrentStreak(newStreak);
|
||||||
|
|
||||||
setPlayers(prev => prev.map(p => {
|
setPlayers(prev => prev.map(p => {
|
||||||
if (p.id !== 'host') return p;
|
if (p.id !== 'host') return p;
|
||||||
return { ...p, score: p.score + points, streak: isCorrect ? p.streak + 1 : 0, lastAnswerCorrect: isCorrect };
|
return { ...p, score: newScore, streak: newStreak, lastAnswerCorrect: isCorrect };
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
const option = arg as AnswerOption;
|
const option = arg as AnswerOption;
|
||||||
|
|
@ -355,7 +369,7 @@ export const useGame = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
role, gameState, quiz, players, currentQuestionIndex, timeLeft, error, gamePin, hasAnswered, lastPointsEarned, currentCorrectShape, selectedOption,
|
role, gameState, quiz, players, currentQuestionIndex, timeLeft, error, gamePin, hasAnswered, lastPointsEarned, currentCorrectShape, selectedOption, currentPlayerScore, currentStreak,
|
||||||
startQuizGen, startManualCreation, finalizeManualQuiz, joinGame, startGame: startHostGame, handleAnswer, nextQuestion
|
startQuizGen, startManualCreation, finalizeManualQuiz, joinGame, startGame: startHostGame, handleAnswer, nextQuestion
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue