Skip to content

Commit

Permalink
Add background and animations
Browse files Browse the repository at this point in the history
  • Loading branch information
CRBl69 authored and xbtmatt committed Jan 29, 2025
1 parent a26fb0d commit 5677bf1
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 166 deletions.
2 changes: 0 additions & 2 deletions src/typescript/frontend/src/app/home/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ArenaCard } from "components/pages/home/components/arena-card";
import EmojiTable from "components/pages/home/components/emoji-table";
import MainCard from "components/pages/home/components/main-card/MainCard";
import { PriceFeed } from "components/price-feed";
import { RandomEmojiBg } from "components/RandomEmojiBg";
import TextCarousel from "components/text-carousel/TextCarousel";
import { type MarketDataSortByHomePage } from "lib/queries/sorting/types";

Expand Down Expand Up @@ -35,7 +34,6 @@ export default async function HomePageComponent({
}: HomePageProps) {
return (
<div className="relative">
<RandomEmojiBg />
<div className="flex-col mb-[31px]">
{priceFeed.length > 0 ? <PriceFeed data={priceFeed} /> : <TextCarousel />}
<div className="flex justify-center items-center px-[16px] mobile-lg:px-[24px] mx-auto w-full max-w-full max-h-[60dvh]">
Expand Down
2 changes: 2 additions & 0 deletions src/typescript/frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "../app/global.css";
import DisplayDebugData from "@/store/server-to-client/FetchFromServer";
import { fontsStyle, notoColorEmoji } from "styles/fonts";
import { headers } from "next/headers";
import { RandomEmojiBg } from "components/RandomEmojiBg";

export const metadata: Metadata = getDefaultMetadata();
export const viewport: Viewport = {
Expand All @@ -18,6 +19,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
return (
<html>
<body>
<RandomEmojiBg />
{/* This is used to avoid React escaping the quotes in `fontsStyle`. */}
<style dangerouslySetInnerHTML={{ __html: fontsStyle }} />
<StyledComponentsRegistry>
Expand Down
111 changes: 84 additions & 27 deletions src/typescript/frontend/src/components/RandomEmojiBg.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,99 @@
"use client";

import useNodeDimensions from "@hooks/use-node-dimensions";
import { getRandomChatEmoji } from "@sdk/emoji_data";
import { useRef } from "react";
import { getRandomSymbolEmoji } from "@sdk/emoji_data";
import { useEffect, useMemo, useRef, useState } from "react";
import { Emoji } from "utils/emoji";

export function RandomEmojiBg() {
function OneRandomEmoji({ emoji }: { emoji: string | undefined }) {
const mt = useMemo(() => Math.random() * 100, []);
const ml = useMemo(() => Math.random() * 100, []);
const rotate = useMemo(() => Math.round(Math.random() * 50 - 25), []);
const [blur, setBlur] = useState(true);
const ref = useRef<HTMLDivElement>(null);
const { height, width } = useNodeDimensions(ref);
const nRows = Math.ceil(height / 100);
const nCols = Math.ceil(width / 100);
const rows = Array.from({ length: nCols * nRows }).map(() => getRandomChatEmoji());
useEffect(() => {
const mouseenter = (e: MouseEvent) => {
const elements = document.elementsFromPoint(e.clientX, e.clientY);
if (elements.find((e) => e === ref.current)) {
setBlur(false);
}
};
const mouseleave = (e: MouseEvent) => {
const elements = document.elementsFromPoint(e.clientX, e.clientY);
if (!elements.find((e) => e === ref.current)) {
setBlur(true);
}
};

if (emoji) {
window.addEventListener("mousemove", mouseenter, { passive: true, capture: true });
window.addEventListener("mousemove", mouseleave, { passive: true, capture: true });

return () => {
window.removeEventListener("mousemove", mouseenter);
window.removeEventListener("mousemove", mouseleave);
};
}
}, [emoji]);
if (!emoji) return <div></div>;
return (
<div
ref={ref}
className="z-[-1] absolute top-0 left-0 h-[100%] w-[100%] grid"
className="flex flex-col w-min h-min text-8xl grid place-items-center select-none transition-all"
style={{
gridTemplateRows: `repeat(${nRows}, 1fr)`,
gridTemplateColumns: `repeat(${nCols}, 1fr)`,
perspective: "20px",
marginTop: `${mt}%`,
marginLeft: `${ml}%`,
transform: `rotate(${rotate}deg)`,
filter: blur ? "blur(15px)" : "",
opacity: blur ? "0.6" : "1",
}}
>
{rows.map((row, i) => {
if (Math.random() > 1 / 20) return <div key={`random-emoji-bg-${i}`}></div>;
return (
<div
className="flex flex-col w-min h-min text-7xl grid place-items-center blur-md hover:blur-none"
style={{
marginTop: `${Math.random() * 100}%`,
marginLeft: `${Math.random() * 100}%`,
transform: `rotate(${Math.round((Math.random() * 50) / 2)}deg)`,
}}
key={`random-emoji-bg-${i}`}
>
{row.emoji}
</div>
);
})}
<Emoji emojis={emoji} />
</div>
);
}

export function RandomEmojiBg() {
const ref = useRef<HTMLDivElement>(null);
const { height, width } = useNodeDimensions(ref);
const nRows = useMemo(() => Math.ceil(height / 150), [height]);
const nCols = useMemo(() => Math.ceil(width / 150), [width]);
const rows = useMemo(
() =>
Array.from({ length: nCols * nRows }).map(() =>
Math.random() < 1 / 8 ? getRandomSymbolEmoji() : undefined
),
[nRows, nCols]
);

const [mt, setMt] = useState<number>(0);

useEffect(() => {
const fn = (e: Event) => {
setMt(((e.target as HTMLElement | null)?.scrollTop ?? 0) * -0.4);
};

document.addEventListener("scroll", fn, { passive: true, capture: true });

return () => document.removeEventListener("scroll", fn);
}, []);

return (
<div className="z-[-1] absolute top-0 left-0 h-[100%] w-[100vw] overflow-hidden">
<div
ref={ref}
className="absolute top-0 left-0 h-[300%] w-[100%] grid"
style={{
gridTemplateRows: `repeat(${nRows}, 1fr)`,
gridTemplateColumns: `repeat(${nCols}, 1fr)`,
perspective: "20px",
top: mt,
}}
>
{rows.map((row, i) => (
<OneRandomEmoji key={`random-emoji-bg-${i}`} emoji={row?.emoji} />
))}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import "./module.css";
import { useInterval } from "react-use";
import { useMemo, useState } from "react";
import darkTheme from "theme/dark";
import { useMatchBreakpoints } from "@hooks/index";
import { getEmojisInString } from "@sdk/emoji_data";

type ArenaCardProps = {
market0Symbol: string;
Expand Down Expand Up @@ -68,7 +70,7 @@ const Timer = ({ startTime, duration }: { startTime: bigint; duration: bigint })
);

return (
<div className="text-light-gray flex flex-row text-6xl">
<div className="text-light-gray flex flex-row pixel-clock w-min">
<TimerNumber n={hours.split("")[0]} />
<TimerNumber n={hours.split("")[1]} />
<div className="my-auto w-[1ch] text-center">:</div>
Expand All @@ -82,11 +84,13 @@ const Timer = ({ startTime, duration }: { startTime: bigint; duration: bigint })
};

const GlowingEmoji = ({ emoji }: { emoji: string }) => (
<div className="relative">
<div className="absolute top-0 z-[-1]" style={{ filter: "blur(15px)" }}>
<Emoji emojis={emoji} />
<div
className={`relative grid items-center place-items-center symbol-${getEmojisInString(emoji).length}`}
>
<div className="absolute z-[-1]" style={{ filter: "blur(15px)" }}>
<Emoji className="flex flex-nowrap text-nowrap" emojis={emoji} />
</div>
<Emoji emojis={emoji} />
<Emoji className="flex flex-nowrap text-nowrap" emojis={emoji} />
</div>
);

Expand All @@ -99,41 +103,56 @@ export const ArenaCard = ({
startTime,
duration,
}: ArenaCardProps) => {
const { isMobile } = useMatchBreakpoints();

const headerText = (
<span
className={`arena-pixel-heading-text text-white uppercase ${isMobile ? "text-center" : ""}`}
>
Lock in early to get the most rewards !
</span>
);

const arenaVs = (
<div
className="grid gap-[.3em]"
style={{
gridTemplateColumns: "1fr auto 1fr",
}}
>
<GlowingEmoji emoji={market0Symbol} />
<span className="vs text-light-gray uppercase m-auto text-[.8em]">vs</span>
<GlowingEmoji emoji={market1Symbol} />
</div>
);

return (
<div className="flex flex-col w-full my-[20px] md:my-[70px] max-w-full">
<div
className="w-full max-w-full"
className="w-full max-w-full gap-[2em]"
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gridTemplateColumns: isMobile ? "1fr" : "1fr 1fr",
gridTemplateRows: !isMobile ? "1fr" : "1fr 1fr",
}}
>
<Link className="place-self-center flex flex-col gap-[3em]" href={ROUTES.arena}>
<div className="flex items-center">
<div className={`flex flex-row gap-[.3em] styled-double-emoji uppercase`}>
<GlowingEmoji emoji={market0Symbol} />
<span className="text-light-gray uppercase m-auto text-[.8em]">vs</span>
<GlowingEmoji emoji={market1Symbol} />
</div>
</div>
<Button scale="xl" className="mx-auto">
Enter now
</Button>
<Link className="place-self-center flex flex-col gap-[3em] w-[100%]" href={ROUTES.arena}>
{isMobile && headerText}
{arenaVs}
{!isMobile && (
<Button scale="xl" className="mx-auto">
Enter now
</Button>
)}
</Link>
<div className="flex flex-col gap-[1em] max-w-full">
<div className="flex flex-row items-center">
<span className="text-white pixel-heading-text uppercase">
Lock in early to get the most rewards !
</span>
</div>
<div className={`flex flex-col gap-[2em] max-w-full ${isMobile ? "items-center" : ""}`}>
{!isMobile && headerText}
<Timer duration={duration} startTime={startTime} />

<div className="flex flex-col">
<div className="flex flex-col gap-[.4em] arena-market-data-text">
<FlexGap gap="8px">
<div className="font-forma text-medium-gray market-data-text uppercase">
Rewards remaining:
</div>
<div className="font-forma text-white market-data-text uppercase">
<div className="font-forma text-medium-gray uppercase">Rewards remaining:</div>
<div className="font-forma text-white uppercase">
<div className="flex flex-row items-center justify-center">
<FormattedNumber value={rewardsRemaining} suffix=" APT" nominalize scramble />
</div>
Expand All @@ -142,22 +161,18 @@ export const ArenaCard = ({

<FlexGap gap="8px">
<div className="uppercase">
<div className="font-forma text-medium-gray market-data-text uppercase">
Melee volume:
</div>
<div className="font-forma text-medium-gray uppercase">Melee volume:</div>
</div>
<div className="font-forma text-white market-data-text uppercase">
<div className="font-forma text-white uppercase">
<div className="flex flex-row items-center justify-center">
<FormattedNumber value={meleeVolume} suffix=" APT" scramble nominalize />
</div>
</div>
</FlexGap>

<FlexGap gap="8px">
<div className="font-forma text-medium-gray market-data-text uppercase">
APT locked:
</div>
<div className="font-forma text-white market-data-text uppercase">
<div className="font-forma text-medium-gray uppercase">APT locked:</div>
<div className="font-forma text-white uppercase">
<div className="flex flex-row items-center justify-center">
<FormattedNumber value={aptLocked} suffix=" APT" scramble nominalize />
</div>
Expand Down
Loading

0 comments on commit 5677bf1

Please sign in to comment.