Skip to content

Commit

Permalink
Add animated chest
Browse files Browse the repository at this point in the history
  • Loading branch information
Matth26 committed Nov 13, 2024
1 parent 3ffaa2d commit a104c62
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 21 deletions.
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"framer-motion": "^11.2.10",
"graphql": "^16.8.2",
"graphql-request": "^6.1.0",
"gsap": "^3.12.5",
"html-to-image": "^1.11.11",
"lucide-react": "^0.394.0",
"mobx": "^6.13.2",
Expand Down
44 changes: 26 additions & 18 deletions client/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 90 additions & 0 deletions client/src/ui/components/AnimatedChest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { useEffect, useRef } from "react";
import { gsap } from "gsap";

interface AnimatedChestProps {
imageSrc: string;
isGrayscale?: boolean;
}

const AnimatedChest: React.FC<AnimatedChestProps> = ({
imageSrc,
isGrayscale = false,
}) => {
const chestRef = useRef<HTMLImageElement>(null);
const particlesRef = useRef<HTMLDivElement>(null);

useEffect(() => {
// Only run animations if not grayscale
if (!isGrayscale) {
// Animate the chest floating
if (chestRef.current) {
gsap.to(chestRef.current, {
y: 20,
duration: 1,
yoyo: true,
repeat: -1,
ease: "power1.inOut",
});
}
// Animate the particles
if (particlesRef.current) {
const particles =
particlesRef.current.querySelectorAll<HTMLElement>(".particle");
particles.forEach((particle) => {
gsap.to(particle, {
x: "random(-200, 200)",
y: "random(-200, 200)",
opacity: 0,
duration: "random(2, 5)",
repeat: -1,
ease: "power1.inOut",
onUpdate: () => {
particle.style.overflow = "hidden";
},
});
});
}
}

// Cleanup function to kill animations when component unmounts or isGrayscale changes
return () => {
if (chestRef.current) {
gsap.killTweensOf(chestRef.current);
}
if (particlesRef.current) {
const particles =
particlesRef.current.querySelectorAll<HTMLElement>(".particle");
particles.forEach((particle) => {
gsap.killTweensOf(particle);
});
}
};
}, [isGrayscale]);

return (
<div className="relative rounded-lg p-4 flex flex-col items-center">
<div ref={particlesRef} className="absolute inset-0 z-0 overflow-hidden">
{!isGrayscale &&
Array.from({ length: 100 }).map((_, index) => (
<div
key={index}
className="particle w-2 h-2 bg-yellow-500 rounded-full absolute"
style={{
top: "35%",
left: "50%",
transform: "translate(-50%, -50%)",
}}
/>
))}
</div>
<img
ref={chestRef}
src={imageSrc}
className={`relative z-10 self-center h-[180px] ${isGrayscale ? "grayscale" : ""}`}
alt="Treasure chest"
/>
</div>
);
};

export default AnimatedChest;
7 changes: 4 additions & 3 deletions client/src/ui/components/TreasureChest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
import { useMediaQuery } from "react-responsive";
import { DialogPrizePoolContributors } from "./DialogPrizePoolContributors";
import { formatPrize } from "@/utils/price";
import AnimatedChest from "./AnimatedChest";

const { VITE_PUBLIC_GAME_TOKEN_SYMBOL } = import.meta.env;

Expand Down Expand Up @@ -85,9 +86,9 @@ const CollectiveTreasureChest: React.FC<CollectiveTreasureChestProps> = ({
>
<ChevronRight className="w-6 h-6" />
</button>
<img
className={`self-center h-[180px] ${currentChest.points === 0 && "grayscale"}`}
src={currentChest.getIcon()}
<AnimatedChest
imageSrc={currentChest.getIcon()}
isGrayscale={currentChest.points === 0}
/>
<div className="relative flex items-center justify-center gap-2 mt-4 w-full">
<div className="text-lg font-semibold text-center flex items-center gap-4">
Expand Down

0 comments on commit a104c62

Please sign in to comment.