Gravity Letters
rare-ui/gravity-lettersFreeComponent
A playful gravity field where letters, numbers, emoji, or any components you pass fall and pile up like real objects.
Install it
npx shadcn@latest add https://rareui.com/r/gravity-letters.jsonSource
1"use client";23import React, { useCallback, useEffect, useRef, useState } from "react";45import { cn } from "@/lib/utils";67export type GlyphType = "letters" | "numbers" | "both";89export type GravityLettersProps = React.ComponentProps<"div"> & {10 type?: GlyphType;11 items?: React.ReactNode[];12 gravity?: number;13 size?: number;14 color?: string;15 maxGlyphs?: number;16 deviceTilt?: boolean;17};1819const POOLS: Record<GlyphType, string> = {20 letters: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",21 numbers: "0123456789",22 both: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",23};2425const COL = 8; // heightmap column width, px26const CLEARANCE = 24; // min air above the landing spot at spawn27const SLOPE = 0.35; // slide on when a neighbor sits this fraction of a glyph lower28const LEAVE_MS = 350;29const TILT = 26; // max rest tilt, deg30const BOUNCE = 0.22; // restitution of the first touch31const HOLD_MS = 300; // hold this long to start pouring32const POUR_MS = 120; // pour cadence while held33const TILT_ON = 10; // device tilt (deg) that starts an avalanche34const SHAKE_MS = 350; // min gap between tilt avalanches35const EAGER = 0.45; // slide-threshold factor while tilted3637const rand = (min: number, max: number) => min + Math.random() * (max - min);3839const clamp = (v: number, min: number, max: number) =>40 Math.min(Math.max(v, min), max);4142const randomChar = (type: GlyphType) => {43 const pool = POOLS[type];44 return pool[Math.floor(Math.random() * pool.length)];45};4647const pickContent = (items: React.ReactNode[] | undefined, type: GlyphType) =>48 items && items.length > 049 ? items[Math.floor(Math.random() * items.length)]50 : randomChar(type);5152const spanOf = (x: number, w: number, cols: number) => {53 const from = Math.max(0, Math.floor(x / COL));54 const to = Math.min(cols - 1, Math.max(from, Math.ceil((x + w) / COL) - 1));55 return [from, to] as const;56};5758const restY = (59 heights: number[],60 x: number,61 w: number,62 h: number,63 rot: number,64 height: number,65) => {66 const [from, to] = spanOf(x, w, heights.length);67 const tan = Math.tan((Math.abs(rot) * Math.PI) / 180);68 let y = Number.POSITIVE_INFINITY;69 for (let i = from; i <= to; i++) {70 const cx = clamp((i + 0.5) * COL - x, 0, w);71 const edge = Math.min((rot >= 0 ? w - cx : cx) * tan, h - 1);72 y = Math.min(y, height - heights[i] - h + edge);73 }74 return y;75};7677const deposit = (78 heights: number[],79 x: number,80 w: number,81 h: number,82 rot: number,83 y: number,84 height: number,85) => {86// … truncated
What it pulls in
Other registry items
Files it writes
More from rare-ui
All 13 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Bounce Sidebarbounce-sidebar | rare-ui | Components | Free | 1 dep | |
| Code Blockcode-block | rare-ui | Components | Free | 3 deps | |
| Duration Pickerduration-picker | rare-ui | Components | Free | 5 deps | |
| Emoji Reactionemoji-reaction | rare-ui | Components | Free | 4 deps | |
| Family Drawerfamily-drawer | rare-ui | Components | Free | 2 deps | |
| Fluid Orbfluid-orb | rare-ui | Components | Free | no deps | |
| Folder Componentfolder-component | rare-ui | Components | Free | 1 dep | |
| GitHub Activitygithub-activity | rare-ui | Components | Free | 1 dep |
