Merge pull request #1 from Ralex91/dev

Update Podium & Sound
This commit is contained in:
Ralex
2024-02-08 15:52:56 +01:00
committed by GitHub
22 changed files with 369 additions and 86 deletions
+38 -1
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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,
}, },
{ {
+5
View File
@@ -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
+48 -10
View File
@@ -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 -2
View File
@@ -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>
+142 -66
View File
@@ -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,91 +66,120 @@ export default function Podium({ data: { subject, top } }) {
}, 2000) }, 2000)
return () => clearInterval(interval) return () => clearInterval(interval)
}, []) }, [apparition])
return ( return (
<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"> {apparition >= 4 && (
{subject} <ReactConfetti
</h2> width={width}
height={height}
className="h-full w-full"
/>
)}
<div {apparition >= 3 && top.length >= 3 && (
className={`grid max-w-[500px] flex-1 grid-cols-${top.length} w-full items-end justify-center justify-self-end overflow-x-visible`} <div className="absolute min-h-screen w-full overflow-hidden">
> <div className="spotlight"></div>{" "}
{top[1] && ( </div>
<div )}
className={clsx( <section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-between">
"z-20 flex h-[50%] w-full flex-col items-center justify-center gap-3 opacity-0", <h2 className="anim-show text-center text-3xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
{ "opacity-100": apparition >= 2 }, {subject}
)} </h2>
>
<p className="overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg">
{top[1].username}
</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">
<p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-zinc-400 bg-zinc-500 text-3xl font-bold text-white drop-shadow-lg">
<span className="drop-shadow-md">2</span>
</p>
<p className="text-2xl font-bold text-white drop-shadow-lg">
{top[1].points}
</p>
</div>
</div>
)}
<div <div
className={clsx( 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`}
"z-30 flex h-[60%] w-full flex-col items-center gap-3 opacity-0",
{
"opacity-100": apparition >= 3,
},
{
"md:min-w-64": top.length < 2,
},
)}
> >
<p {top[1] && (
className={clsx( <div
"overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white opacity-0 drop-shadow-lg", className={clsx(
{ "opacity-100": apparition >= 4 }, "z-20 flex h-[50%] w-full translate-y-full flex-col items-center justify-center gap-3 opacity-0 transition-all",
)} { "!translate-y-0 opacity-100": apparition >= 2 },
> )}
{top[0].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"> className={clsx(
<p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-amber-400 bg-amber-300 text-3xl font-bold text-white drop-shadow-lg"> "overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg md:text-4xl",
<span className="drop-shadow-md">1</span> {
</p> "anim-balanced": apparition >= 4,
<p className="text-2xl font-bold text-white drop-shadow-lg"> },
{top[0].points} )}
</p> >
</div> {top[1].username}
</div> </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">
<p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-zinc-400 bg-zinc-500 text-3xl font-bold text-white drop-shadow-lg">
<span className="drop-shadow-md">2</span>
</p>
<p className="text-2xl font-bold text-white drop-shadow-lg">
{top[1].points}
</p>
</div>
</div>
)}
{top[2] && (
<div <div
className={clsx( className={clsx(
"z-10 flex h-[40%] 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 >= 1, "!translate-y-0 opacity-100": apparition >= 3,
},
{
"md:min-w-64": top.length < 2,
}, },
)} )}
> >
<p className="overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg"> <p
{top[2].username} className={clsx(
"overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white opacity-0 drop-shadow-lg md:text-4xl",
{ "anim-balanced opacity-100": apparition >= 4 },
)}
>
{top[0].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">
<p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-amber-800 bg-amber-700 text-3xl font-bold text-white drop-shadow-lg"> <p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-amber-400 bg-amber-300 text-3xl font-bold text-white drop-shadow-lg">
<span className="drop-shadow-md">3</span> <span className="drop-shadow-md">1</span>
</p> </p>
<p className="text-2xl font-bold text-white drop-shadow-lg"> <p className="text-2xl font-bold text-white drop-shadow-lg">
{top[2].points} {top[0].points}
</p> </p>
</div> </div>
</div> </div>
)}
</div> {top[2] && (
</section> <div
className={clsx(
"z-10 flex h-[40%] w-full translate-y-full flex-col items-center gap-3 opacity-0 transition-all",
{
"!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,
},
)}
>
{top[2].username}
</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">
<p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-amber-800 bg-amber-700 text-3xl font-bold text-white drop-shadow-lg">
<span className="drop-shadow-md">3</span>
</p>
<p className="text-2xl font-bold text-white drop-shadow-lg">
{top[2].points}
</p>
</div>
</div>
)}
</div>
</section>
</>
) )
} }
+8 -3
View File
@@ -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>
+9 -1
View File
@@ -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">
+9 -1
View File
@@ -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">
+10
View File
@@ -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"
+26
View File
@@ -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
+65
View File
@@ -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);