mirror of
https://github.com/randyjc/Rahoot.git
synced 2026-09-08 12:13:15 +02:00
Generated
+38
-1
@@ -11,10 +11,12 @@
|
|||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"next": "14.1.0",
|
"next": "14.1.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
"react-confetti": "^6.1.0",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"socket.io": "^4.7.4",
|
"socket.io": "^4.7.4",
|
||||||
"socket.io-client": "^4.7.4"
|
"socket.io-client": "^4.7.4",
|
||||||
|
"use-sound": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
@@ -2422,6 +2424,11 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/howler": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/howler/-/howler-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w=="
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
|
||||||
@@ -3823,6 +3830,20 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-confetti": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-confetti/-/react-confetti-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tween-functions": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.3.0 || ^17.0.1 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||||
@@ -4610,6 +4631,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/tween-functions": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA=="
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
@@ -4772,6 +4798,17 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sound": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sound/-/use-sound-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-hykJ86kNcu6y/FzlSHcQxhjSGMslZx2WlfLpZNoPbvueakv4OF3xPxEtGV2YmculrIaH0tPp9LtG4jgy17xMWg==",
|
||||||
|
"dependencies": {
|
||||||
|
"howler": "^2.1.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
|||||||
+3
-1
@@ -12,10 +12,12 @@
|
|||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"next": "14.1.0",
|
"next": "14.1.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
"react-confetti": "^6.1.0",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"socket.io": "^4.7.4",
|
"socket.io": "^4.7.4",
|
||||||
"socket.io-client": "^4.7.4"
|
"socket.io-client": "^4.7.4",
|
||||||
|
"use-sound": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -18,7 +18,7 @@ export const GAME_STATE_INIT = {
|
|||||||
"Bill Gate",
|
"Bill Gate",
|
||||||
],
|
],
|
||||||
solution: 1,
|
solution: 1,
|
||||||
cooldow: 5,
|
cooldown: 5,
|
||||||
time: 15,
|
time: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ const Player = {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (game.players.find((p) => p.username === player.username)) {
|
||||||
|
socket.emit("game:errorMessage", "Username already exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (game.started) {
|
if (game.started) {
|
||||||
socket.emit("game:errorMessage", "Game already started")
|
socket.emit("game:errorMessage", "Game already started")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,7 +2,14 @@ import AnswerButton from "../../AnswerButton"
|
|||||||
import { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { ANSWERS_COLORS, ANSWERS_ICONS } from "@/constants"
|
import {
|
||||||
|
ANSWERS_COLORS,
|
||||||
|
ANSWERS_ICONS,
|
||||||
|
SFX_ANSWERS_MUSIC,
|
||||||
|
SFX_ANSWERS_SOUND,
|
||||||
|
SFX_RESULTS_SOUND,
|
||||||
|
} from "@/constants"
|
||||||
|
import useSound from "use-sound"
|
||||||
|
|
||||||
const calculatePercentages = (objectResponses) => {
|
const calculatePercentages = (objectResponses) => {
|
||||||
const keys = Object.keys(objectResponses)
|
const keys = Object.keys(objectResponses)
|
||||||
@@ -35,13 +42,49 @@ export default function Answers({
|
|||||||
const [cooldown, setCooldown] = useState(time)
|
const [cooldown, setCooldown] = useState(time)
|
||||||
const [totalAnswer, setTotalAnswer] = useState(0)
|
const [totalAnswer, setTotalAnswer] = useState(0)
|
||||||
|
|
||||||
|
const [sfxPop] = useSound(SFX_ANSWERS_SOUND, {
|
||||||
|
volume: 0.1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [sfxResults] = useSound(SFX_RESULTS_SOUND, {
|
||||||
|
volume: 0.2,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [playMusic, { stop: stopMusic, isPlaying }] = useSound(
|
||||||
|
SFX_ANSWERS_MUSIC,
|
||||||
|
{
|
||||||
|
volume: 0.2,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleAnswer = (answer) => {
|
||||||
|
socket.emit("player:selectedAnswer", answer)
|
||||||
|
sfxPop()
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!responses) {
|
if (!responses) {
|
||||||
|
playMusic()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopMusic()
|
||||||
|
sfxResults()
|
||||||
|
|
||||||
setPercentages(calculatePercentages(responses))
|
setPercentages(calculatePercentages(responses))
|
||||||
}, [responses])
|
}, [responses, playMusic, stopMusic])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isPlaying) {
|
||||||
|
playMusic()
|
||||||
|
}
|
||||||
|
}, [isPlaying])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
stopMusic()
|
||||||
|
}
|
||||||
|
}, [playMusic, stopMusic])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket.on("game:cooldown", (sec) => {
|
socket.on("game:cooldown", (sec) => {
|
||||||
@@ -50,13 +93,14 @@ export default function Answers({
|
|||||||
|
|
||||||
socket.on("game:playerAnswer", (count) => {
|
socket.on("game:playerAnswer", (count) => {
|
||||||
setTotalAnswer(count)
|
setTotalAnswer(count)
|
||||||
|
sfxPop()
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.off("game:cooldown")
|
socket.off("game:cooldown")
|
||||||
socket.off("game:playerAnswer")
|
socket.off("game:playerAnswer")
|
||||||
}
|
}
|
||||||
}, [])
|
}, [sfxPop])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-1 flex-col justify-between">
|
<div className="flex h-full flex-1 flex-col justify-between">
|
||||||
@@ -113,7 +157,7 @@ export default function Answers({
|
|||||||
"opacity-65": responses && correct !== key,
|
"opacity-65": responses && correct !== key,
|
||||||
})}
|
})}
|
||||||
icon={ANSWERS_ICONS[key]}
|
icon={ANSWERS_ICONS[key]}
|
||||||
onClick={() => socket.emit("player:selectedAnswer", key)}
|
onClick={() => handleAnswer(key)}
|
||||||
>
|
>
|
||||||
{answer}
|
{answer}
|
||||||
</AnswerButton>
|
</AnswerButton>
|
||||||
@@ -123,9 +167,3 @@ export default function Answers({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OLD Timer
|
|
||||||
<div className="absolute left-8 -translate-y-1/2 top-2/4 text-white font-bold text-6xl rounded-full justify-center items-center bg-orange-400 p-8 aspect-square hidden 2xl:flex">
|
|
||||||
<span </div>className="drop-shadow-md">20</span>
|
|
||||||
</div>
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ export default function Leaderboard({ data: { leaderboard } }) {
|
|||||||
Leaderboard
|
Leaderboard
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex w-full flex-col gap-2">
|
<div className="flex w-full flex-col gap-2">
|
||||||
{leaderboard.map(({ username, points }) => (
|
{leaderboard.map(({ username, points }, key) => (
|
||||||
<div className="flex w-full justify-between rounded-md bg-primary p-3 text-2xl font-bold text-white">
|
<div
|
||||||
|
key={key}
|
||||||
|
className="flex w-full justify-between rounded-md bg-primary p-3 text-2xl font-bold text-white"
|
||||||
|
>
|
||||||
<span className="drop-shadow-md">{username}</span>
|
<span className="drop-shadow-md">{username}</span>
|
||||||
<span className="drop-shadow-md">{points}</span>
|
<span className="drop-shadow-md">{points}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,60 @@
|
|||||||
import Loader from "@/components/Loader"
|
import Loader from "@/components/Loader"
|
||||||
|
import {
|
||||||
|
SFX_PODIUM_FIRST,
|
||||||
|
SFX_PODIUM_SECOND,
|
||||||
|
SFX_PODIUM_THREE,
|
||||||
|
SFX_SNEAR_ROOL,
|
||||||
|
} from "@/constants"
|
||||||
|
import useScreenSize from "@/hook/useScreenSize"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
import ReactConfetti from "react-confetti"
|
||||||
|
import useSound from "use-sound"
|
||||||
|
|
||||||
export default function Podium({ data: { subject, top } }) {
|
export default function Podium({ data: { subject, top } }) {
|
||||||
const [apparition, setApparition] = useState(0)
|
const [apparition, setApparition] = useState(0)
|
||||||
|
|
||||||
|
const { width, height } = useScreenSize()
|
||||||
|
|
||||||
|
const [sfxtThree] = useSound(SFX_PODIUM_THREE, {
|
||||||
|
volume: 0.2,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [sfxSecond] = useSound(SFX_PODIUM_SECOND, {
|
||||||
|
volume: 0.2,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [sfxRool, { stop: sfxRoolStop }] = useSound(SFX_SNEAR_ROOL, {
|
||||||
|
volume: 0.2,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [sfxFirst] = useSound(SFX_PODIUM_FIRST, {
|
||||||
|
volume: 0.2,
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(apparition)
|
||||||
|
switch (apparition) {
|
||||||
|
case 4:
|
||||||
|
sfxRoolStop()
|
||||||
|
sfxFirst()
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
sfxRool()
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
sfxSecond()
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
sfxtThree()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}, [apparition, sfxFirst, sfxSecond, sfxtThree, sfxRool])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (top.length < 3) {
|
if (top.length < 3) {
|
||||||
setApparition(4)
|
setApparition(4)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
@@ -19,25 +66,46 @@ export default function Podium({ data: { subject, top } }) {
|
|||||||
}, 2000)
|
}, 2000)
|
||||||
|
|
||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [])
|
}, [apparition])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{apparition >= 4 && (
|
||||||
|
<ReactConfetti
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
className="h-full w-full"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{apparition >= 3 && top.length >= 3 && (
|
||||||
|
<div className="absolute min-h-screen w-full overflow-hidden">
|
||||||
|
<div className="spotlight"></div>{" "}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-between">
|
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-between">
|
||||||
<h2 className="anim-show text-center text-3xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
<h2 className="anim-show text-center text-3xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
||||||
{subject}
|
{subject}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`grid max-w-[500px] flex-1 grid-cols-${top.length} w-full items-end justify-center justify-self-end overflow-x-visible`}
|
className={`grid w-full max-w-[800px] flex-1 grid-cols-${top.length} items-end justify-center justify-self-end overflow-y-hidden overflow-x-visible`}
|
||||||
>
|
>
|
||||||
{top[1] && (
|
{top[1] && (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"z-20 flex h-[50%] w-full flex-col items-center justify-center gap-3 opacity-0",
|
"z-20 flex h-[50%] w-full translate-y-full flex-col items-center justify-center gap-3 opacity-0 transition-all",
|
||||||
{ "opacity-100": apparition >= 2 },
|
{ "!translate-y-0 opacity-100": apparition >= 2 },
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
className={clsx(
|
||||||
|
"overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg md:text-4xl",
|
||||||
|
{
|
||||||
|
"anim-balanced": apparition >= 4,
|
||||||
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<p className="overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg">
|
|
||||||
{top[1].username}
|
{top[1].username}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex h-full w-full flex-col items-center gap-4 rounded-t-md bg-primary pt-6 text-center shadow-2xl">
|
<div className="flex h-full w-full flex-col items-center gap-4 rounded-t-md bg-primary pt-6 text-center shadow-2xl">
|
||||||
@@ -53,9 +121,9 @@ export default function Podium({ data: { subject, top } }) {
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"z-30 flex h-[60%] w-full flex-col items-center gap-3 opacity-0",
|
"z-30 flex h-[60%] w-full translate-y-full flex-col items-center gap-3 opacity-0 transition-all",
|
||||||
{
|
{
|
||||||
"opacity-100": apparition >= 3,
|
"!translate-y-0 opacity-100": apparition >= 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"md:min-w-64": top.length < 2,
|
"md:min-w-64": top.length < 2,
|
||||||
@@ -64,8 +132,8 @@ export default function Podium({ data: { subject, top } }) {
|
|||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white opacity-0 drop-shadow-lg",
|
"overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white opacity-0 drop-shadow-lg md:text-4xl",
|
||||||
{ "opacity-100": apparition >= 4 },
|
{ "anim-balanced opacity-100": apparition >= 4 },
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{top[0].username}
|
{top[0].username}
|
||||||
@@ -83,13 +151,20 @@ export default function Podium({ data: { subject, top } }) {
|
|||||||
{top[2] && (
|
{top[2] && (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"z-10 flex h-[40%] w-full flex-col items-center gap-3 opacity-0",
|
"z-10 flex h-[40%] w-full translate-y-full flex-col items-center gap-3 opacity-0 transition-all",
|
||||||
{
|
{
|
||||||
"opacity-100": apparition >= 1,
|
"!translate-y-0 opacity-100": apparition >= 1,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
className={clsx(
|
||||||
|
"overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg md:text-4xl",
|
||||||
|
{
|
||||||
|
"anim-balanced": apparition >= 4,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<p className="overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg">
|
|
||||||
{top[2].username}
|
{top[2].username}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex h-full w-full flex-col items-center gap-4 rounded-t-md bg-primary pt-6 text-center shadow-2xl">
|
<div className="flex h-full w-full flex-col items-center gap-4 rounded-t-md bg-primary pt-6 text-center shadow-2xl">
|
||||||
@@ -105,5 +180,6 @@ export default function Podium({ data: { subject, top } }) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import { useRef } from "react"
|
import { SFX_SHOW_SOUND } from "@/constants"
|
||||||
|
import { useEffect, useRef } from "react"
|
||||||
|
import useSound from "use-sound"
|
||||||
|
|
||||||
export default function Question({ data: { question, image, cooldown } }) {
|
export default function Question({ data: { question, image, cooldown } }) {
|
||||||
const barRef = useRef(null)
|
const [sfxShow] = useSound(SFX_SHOW_SOUND, { volume: 0.5 })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sfxShow()
|
||||||
|
}, [sfxShow])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative mx-auto flex h-full w-full max-w-7xl flex-1 flex-col items-center px-4">
|
<section className="relative mx-auto flex h-full w-full max-w-7xl flex-1 flex-col items-center px-4">
|
||||||
@@ -15,7 +21,6 @@ export default function Question({ data: { question, image, cooldown } }) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref={barRef}
|
|
||||||
className="mb-20 h-4 self-start justify-self-end rounded-full bg-primary"
|
className="mb-20 h-4 self-start justify-self-end rounded-full bg-primary"
|
||||||
style={{ animation: `progressBar ${cooldown}s linear forwards` }}
|
style={{ animation: `progressBar ${cooldown}s linear forwards` }}
|
||||||
></div>
|
></div>
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
import CricleCheck from "@/components/icons/CricleCheck"
|
import CricleCheck from "@/components/icons/CricleCheck"
|
||||||
import CricleXmark from "@/components/icons/CricleXmark"
|
import CricleXmark from "@/components/icons/CricleXmark"
|
||||||
|
import { SFX_RESULTS_SOUND } from "@/constants"
|
||||||
import { usePlayerContext } from "@/context/player"
|
import { usePlayerContext } from "@/context/player"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
|
import useSound from "use-sound"
|
||||||
|
|
||||||
export default function Result({
|
export default function Result({
|
||||||
data: { correct, message, points, myPoints, totalPlayer, rank, aheadOfMe },
|
data: { correct, message, points, myPoints, totalPlayer, rank, aheadOfMe },
|
||||||
}) {
|
}) {
|
||||||
const { dispatch } = usePlayerContext()
|
const { dispatch } = usePlayerContext()
|
||||||
|
|
||||||
|
const [sfxResults] = useSound(SFX_RESULTS_SOUND, {
|
||||||
|
volume: 0.2,
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "UPDATE",
|
type: "UPDATE",
|
||||||
payload: { points: myPoints },
|
payload: { points: myPoints },
|
||||||
})
|
})
|
||||||
}, [])
|
|
||||||
|
sfxResults()
|
||||||
|
}, [sfxResults])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="anim-show relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
<section className="anim-show relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
|
import { SFX_BOUMP_SOUND } from "@/constants"
|
||||||
import { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
import useSound from "use-sound"
|
||||||
|
|
||||||
export default function Start({ data: { time, subject } }) {
|
export default function Start({ data: { time, subject } }) {
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
const [showTitle, setShowTitle] = useState(true)
|
const [showTitle, setShowTitle] = useState(true)
|
||||||
const [cooldown, setCooldown] = useState(time)
|
const [cooldown, setCooldown] = useState(time)
|
||||||
|
|
||||||
|
const [sfxBoump] = useSound(SFX_BOUMP_SOUND, {
|
||||||
|
volume: 0.2,
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket.on("game:startCooldown", () => {
|
socket.on("game:startCooldown", () => {
|
||||||
|
sfxBoump()
|
||||||
setShowTitle(false)
|
setShowTitle(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("game:cooldown", (sec) => {
|
socket.on("game:cooldown", (sec) => {
|
||||||
|
sfxBoump()
|
||||||
setCooldown(sec)
|
setCooldown(sec)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -20,7 +28,7 @@ export default function Start({ data: { time, subject } }) {
|
|||||||
socket.off("game:startCooldown")
|
socket.off("game:startCooldown")
|
||||||
socket.off("game:cooldown")
|
socket.off("game:cooldown")
|
||||||
}
|
}
|
||||||
}, [])
|
}, [sfxBoump])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
||||||
|
|||||||
@@ -51,3 +51,13 @@ export const GAME_STATE_COMPONENTS_MANAGER = {
|
|||||||
SHOW_LEADERBOARD: Leaderboard,
|
SHOW_LEADERBOARD: Leaderboard,
|
||||||
FINISH: Podium,
|
FINISH: Podium,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SFX_ANSWERS_MUSIC = "/sounds/answersMusic.mp3"
|
||||||
|
export const SFX_ANSWERS_SOUND = "/sounds/answersSound.mp3"
|
||||||
|
export const SFX_RESULTS_SOUND = "/sounds/results.mp3"
|
||||||
|
export const SFX_SHOW_SOUND = "/sounds/show.mp3"
|
||||||
|
export const SFX_BOUMP_SOUND = "/sounds/boump.mp3"
|
||||||
|
export const SFX_PODIUM_THREE = "/sounds/three.mp3"
|
||||||
|
export const SFX_PODIUM_SECOND = "/sounds/second.mp3"
|
||||||
|
export const SFX_PODIUM_FIRST = "/sounds/first.mp3"
|
||||||
|
export const SFX_SNEAR_ROOL = "/sounds/snearRoll.mp3"
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { useState, useEffect } from "react"
|
||||||
|
|
||||||
|
const useScreenSize = () => {
|
||||||
|
const [screenSize, setScreenSize] = useState({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleResize = () => {
|
||||||
|
setScreenSize({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", handleResize)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", handleResize)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return screenSize
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useScreenSize
|
||||||
@@ -32,6 +32,49 @@
|
|||||||
box-shadow: rgba(0, 0, 0, 0.25) 0px -4px inset;
|
box-shadow: rgba(0, 0, 0, 0.25) 0px -4px inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spotlight {
|
||||||
|
position: absolute;
|
||||||
|
height: 200%;
|
||||||
|
width: 200%;
|
||||||
|
z-index: 100;
|
||||||
|
background-image: radial-gradient(
|
||||||
|
circle,
|
||||||
|
transparent 180px,
|
||||||
|
rgba(0, 0, 0, 0.6) 200px
|
||||||
|
);
|
||||||
|
opacity: 0;
|
||||||
|
left: -50%;
|
||||||
|
top: -50%;
|
||||||
|
transition: all 0.5s;
|
||||||
|
animation: spotlightAnim 2.5s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spotlightAnim {
|
||||||
|
0% {
|
||||||
|
left: -20%;
|
||||||
|
top: -20%;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
opacity: 100;
|
||||||
|
top: -80%;
|
||||||
|
left: -80%;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
top: -50%;
|
||||||
|
left: -20%;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
top: -50%;
|
||||||
|
left: -50%;
|
||||||
|
}
|
||||||
|
98% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.anim-show {
|
.anim-show {
|
||||||
animation: show 0.5s ease-out;
|
animation: show 0.5s ease-out;
|
||||||
}
|
}
|
||||||
@@ -52,6 +95,28 @@
|
|||||||
animation: quizzButton 0.8s ease-out;
|
animation: quizzButton 0.8s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.anim-balanced {
|
||||||
|
animation: balanced 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes balanced {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: rotate(-10deg) translateY(-10px);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotate(0deg) translateY(0px);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: rotate(10deg) translateY(-10px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes show {
|
@keyframes show {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user