diff --git a/bad b/bad deleted file mode 100644 index 1b6af5b..0000000 --- a/bad +++ /dev/null @@ -1 +0,0 @@ -f7360d42-dcc1-4e1e-a90b-ef2295298872 diff --git a/bun.lockb b/bun.lockb index e7afa93..ab2a726 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 8c4363d..cba12a9 100644 --- a/package.json +++ b/package.json @@ -35,9 +35,9 @@ "clsx": "^2.1.1", "dayjs": "^1.11.13", "drizzle-orm": "0.33.0", - "framer-motion": "^11.11.8", "jotai": "^2.10.0", "lucide-react": "^0.452.0", + "motion": "^12.18.1", "pixi-viewport": "^5.0.3", "pixi.js": "^7.0.0", "pixi.js-legacy": "^7.4.2", @@ -69,6 +69,7 @@ "typescript": "^5.6.3", "typescript-eslint": "^8.8.1", "vite": "^5.4.8", + "vite-bundle-analyzer": "^0.22.3", "vite-imagetools": "^7.0.4" } } diff --git a/src/Shell.tsx b/src/Shell.tsx index 33cbcc2..4a13e73 100644 --- a/src/Shell.tsx +++ b/src/Shell.tsx @@ -1,6 +1,6 @@ import { type PropsWithChildren, useEffect, useRef, useState } from "react"; import { Button } from "./components/Button"; -import { motion } from "framer-motion"; +import { motion } from "motion/react"; import { GitBranch, History, diff --git a/src/components/Board.tsx b/src/components/Board.tsx index e4f6d11..add2d08 100644 --- a/src/components/Board.tsx +++ b/src/components/Board.tsx @@ -10,8 +10,8 @@ import { type LoadedTexture, type LoadedTheme, type Theme, - useTheme, } from "../themes/Theme"; +import { useTheme } from "../themes/useTheme"; import { Container, Sprite, Stage, useTick } from "@pixi/react"; import Viewport from "./pixi/PixiViewport"; import type { Viewport as PixiViewport } from "pixi-viewport"; diff --git a/src/components/BounceImg.tsx b/src/components/BounceImg.tsx index 5818449..0a4f21c 100644 --- a/src/components/BounceImg.tsx +++ b/src/components/BounceImg.tsx @@ -1,4 +1,4 @@ -import { animate, motion } from "framer-motion"; +import { animate, motion } from "motion/react"; import { useRef } from "react"; const BounceImg = ({ src, className }: { src: string; className?: string }) => { diff --git a/src/components/Feed/Feed.tsx b/src/components/Feed/Feed.tsx index 824ca57..7c1e465 100644 --- a/src/components/Feed/Feed.tsx +++ b/src/components/Feed/Feed.tsx @@ -1,4 +1,4 @@ -import { AnimatePresence, motion } from "framer-motion"; +import { AnimatePresence, motion } from "motion/react"; import { useAtom } from "jotai"; import { feedItemsAtom, lootboxResultAtom } from "../../atoms"; import FeedItemElement from "./FeedItem"; diff --git a/src/components/Feed/FeedItem.tsx b/src/components/Feed/FeedItem.tsx index 1d99dc0..c74b393 100644 --- a/src/components/Feed/FeedItem.tsx +++ b/src/components/Feed/FeedItem.tsx @@ -1,4 +1,4 @@ -import { motion } from "framer-motion"; +import { motion } from "motion/react"; import type { PropsWithChildren } from "react"; import { formatTimeSpan } from "../../../shared/time"; import GemsIcon from "../GemIcon"; diff --git a/src/components/LazyBoard.tsx b/src/components/LazyBoard.tsx new file mode 100644 index 0000000..11bf3aa --- /dev/null +++ b/src/components/LazyBoard.tsx @@ -0,0 +1,3 @@ +import { lazy } from "react"; + +export const Board = lazy(() => import("./Board")); diff --git a/src/main.tsx b/src/main.tsx index f9b9815..acbe2c0 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -12,7 +12,7 @@ import Home from "./views/home/Home.tsx"; import Settings from "./views/settings/Settings.tsx"; import MatchHistory from "./views/match-history/MatchHistory.tsx"; import Collection from "./views/collection/Collection.tsx"; -import { AnimatePresence } from "framer-motion"; +import { AnimatePresence } from "motion/react"; import Store from "./views/store/Store.tsx"; import Profile from "./views/profile/Profile.tsx"; diff --git a/src/themes/Theme.ts b/src/themes/Theme.ts index 432eae0..b20507a 100644 --- a/src/themes/Theme.ts +++ b/src/themes/Theme.ts @@ -1,10 +1,9 @@ -import { Assets, Texture } from "pixi.js"; -import { useEffect, useState } from "react"; +import type { Texture } from "pixi.js"; type Png = typeof import("*.png"); type LazySprite = () => Promise; -interface WeightedLazySprites { +export interface WeightedLazySprites { weight: number; sprite: LazySprite; } @@ -54,36 +53,3 @@ export const mainWithSpecials = ( ...specials.map((sprite) => ({ weight: 0.05, sprite })), ]; }; - -export const useTheme = (theme: Theme) => { - const [loadedTheme, setLoadedTheme] = useState( - undefined - ); - useEffect(() => { - const loadTheme = async () => { - const loadedEntries = await Promise.all( - Object.entries(theme).map(async ([key, value]) => { - let loaded = value; - if (typeof value === "function") { - loaded = await Assets.load((await value()).default); - } - if (Array.isArray(value)) { - loaded = await Promise.all( - loaded.map(async (sprite: WeightedLazySprites) => { - return { - weight: sprite.weight, - sprite: await Assets.load((await sprite.sprite()).default), - }; - }) - ); - } - - return [key, loaded] as const; - }) - ); - setLoadedTheme(Object.fromEntries(loadedEntries) as LoadedTheme); - }; - loadTheme(); - }, [theme]); - return loadedTheme; -}; diff --git a/src/themes/useTheme.ts b/src/themes/useTheme.ts new file mode 100644 index 0000000..ea7e76f --- /dev/null +++ b/src/themes/useTheme.ts @@ -0,0 +1,36 @@ +import { Assets } from "pixi.js"; +import { useState, useEffect } from "react"; +import type { Theme, LoadedTheme, WeightedLazySprites } from "./Theme"; + +export const useTheme = (theme: Theme) => { + const [loadedTheme, setLoadedTheme] = useState( + undefined, + ); + useEffect(() => { + const loadTheme = async () => { + const loadedEntries = await Promise.all( + Object.entries(theme).map(async ([key, value]) => { + let loaded = value; + if (typeof value === "function") { + loaded = await Assets.load((await value()).default); + } + if (Array.isArray(value)) { + loaded = await Promise.all( + loaded.map(async (sprite: WeightedLazySprites) => { + return { + weight: sprite.weight, + sprite: await Assets.load((await sprite.sprite()).default), + }; + }), + ); + } + + return [key, loaded] as const; + }), + ); + setLoadedTheme(Object.fromEntries(loadedEntries) as LoadedTheme); + }; + loadTheme(); + }, [theme]); + return loadedTheme; +}; diff --git a/src/views/collection/Collection.tsx b/src/views/collection/Collection.tsx index 92a5193..2e528a4 100644 --- a/src/views/collection/Collection.tsx +++ b/src/views/collection/Collection.tsx @@ -1,6 +1,6 @@ import { Ellipsis } from "lucide-react"; import { testBoard } from "../../../shared/testBoard"; -import Board from "../../components/Board"; +import { Board } from "../../components/LazyBoard"; import { Button } from "../../components/Button"; import { themes } from "../../themes"; import { diff --git a/src/views/endless/Endless.tsx b/src/views/endless/Endless.tsx index d61ebcb..ef80503 100644 --- a/src/views/endless/Endless.tsx +++ b/src/views/endless/Endless.tsx @@ -1,10 +1,10 @@ -import Board from "../../components/Board"; import { useWSMutation, useWSQuery } from "../../hooks"; import { useAtom } from "jotai"; import { gameIdAtom } from "../../atoms"; import { Button } from "../../components/Button"; import LeaderboardButton from "../../components/LeaderboardButton"; import { Fragment, useEffect } from "react"; +import { Board } from "../../components/LazyBoard"; interface EndlessProps { gameId?: string; diff --git a/src/views/home/Home.tsx b/src/views/home/Home.tsx index d444161..10d341e 100644 --- a/src/views/home/Home.tsx +++ b/src/views/home/Home.tsx @@ -1,4 +1,4 @@ -import { animate, motion, useMotionValue, useTransform } from "framer-motion"; +import { animate, motion, useMotionValue, useTransform } from "motion/react"; import { useEffect } from "react"; import { useWSQuery } from "../../hooks"; import { Tag } from "../../components/Tag"; diff --git a/src/views/home/Section.tsx b/src/views/home/Section.tsx index ad0cd5e..44dfdc8 100644 --- a/src/views/home/Section.tsx +++ b/src/views/home/Section.tsx @@ -4,7 +4,7 @@ import { useMotionTemplate, useScroll, useTransform, -} from "framer-motion"; +} from "motion/react"; import { useEffect, useRef, useState } from "react"; import { cn } from "../../lib/utils"; @@ -48,8 +48,10 @@ const Section = ({ text, image, left }: SectionProps) => { className="md:w-[50%] h-90" // float up and down animate={{ - translateY: [0, 10, 0], - translateX: [0, 5, 0], + // translate: ["0 0", "5 10", "0 0"], + // transform: ["translate"] + x: [0, 10, 0], + y: [0, 5, 0], }} transition={{ repeat: Infinity, @@ -71,7 +73,7 @@ const Section = ({ text, image, left }: SectionProps) => { translateY, }} transition={{ - type: "just", + type: "spring", delay: 0.5, }} srcSet={image.map((i) => `${i.src} ${i.width}w`).join(", ")} diff --git a/src/views/profile/Profile.tsx b/src/views/profile/Profile.tsx index 7b8c4a9..03f3754 100644 --- a/src/views/profile/Profile.tsx +++ b/src/views/profile/Profile.tsx @@ -40,7 +40,9 @@ const Profile: React.FC = () => {

Total Games: {profile?.totalGames}

Highest Stage: {profile?.highestStage}

-

Average Stage: {profile?.averageStage}

+

+ Average Stage: {Math.round(profile?.averageStage ?? 1 * 100) / 100} +

diff --git a/src/views/store/Store.tsx b/src/views/store/Store.tsx index 6e147e6..f589781 100644 --- a/src/views/store/Store.tsx +++ b/src/views/store/Store.tsx @@ -16,9 +16,7 @@ import { lootboxResultAtom } from "../../atoms"; import { useAtom } from "jotai"; import { useEffect } from "react"; import Particles, { initParticlesEngine } from "@tsparticles/react"; -import { loadSlim } from "@tsparticles/slim"; -import { loadSeaAnemonePreset } from "@tsparticles/preset-sea-anemone"; -import { motion } from "framer-motion"; +import { motion } from "motion/react"; import BounceImg from "../../components/BounceImg"; const Store = () => { @@ -29,17 +27,24 @@ const Store = () => { // this should be run only once per application lifetime useEffect(() => { - initParticlesEngine(async (engine) => { - // you can initiate the tsParticles instance (engine) here, adding custom shapes or presets - // this loads the tsparticles package bundle, it's the easiest method for getting everything ready - // starting from v2 you can add only the features you need reducing the bundle size - //await loadAll(engine); - //await loadFull(engine); - await loadSlim(engine); - await loadSeaAnemonePreset(engine); + const cb = async () => { + const { loadSlim } = await import("@tsparticles/slim"); + const { loadSeaAnemonePreset } = await import( + "@tsparticles/preset-sea-anemone" + ); + initParticlesEngine(async (engine) => { + // you can initiate the tsParticles instance (engine) here, adding custom shapes or presets + // this loads the tsparticles package bundle, it's the easiest method for getting everything ready + // starting from v2 you can add only the features you need reducing the bundle size + //await loadAll(engine); + //await loadFull(engine); + await loadSlim(engine); + await loadSeaAnemonePreset(engine); - //await loadBasic(engine); - }); + //await loadBasic(engine); + }); + }; + cb(); }, []); return ( diff --git a/vite.config.ts b/vite.config.ts index 9ffaf19..34e8257 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,11 +2,12 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import tailwindcss from "@tailwindcss/vite"; import { imagetools } from "vite-imagetools"; +import { analyzer } from "vite-bundle-analyzer"; // https://vitejs.dev/config/ export default defineConfig({ server: { port: 3003, }, - plugins: [react(), tailwindcss(), imagetools()], + plugins: [react(), tailwindcss(), imagetools(), analyzer()], });