Twin Perimeter
cellforge/cell-angle-20FreeComponent
Two stepped heads chase the outer perimeter path half a lap apart, each with its own tail; the heart cell stays quietly dim off the loop.
Install it
npx shadcn@latest add https://cellforge.dev/r/cell-angle-20.jsonSource
1"use client";23import type { CSSProperties } from "react";45import { cx } from "@/components/ui/cellforge-core";6import { resolveDmxColorTokens } from "@/components/ui/cellforge-core";7import { useCellForgePhases } from "@/components/ui/cellforge-hooks";8import { styleOpacity, stylePx } from "@/components/ui/cellforge-core";9import { remapOpacityToTriplet } from "@/components/ui/cellforge-core";10import { dmxBloomRootActive, dmxDotBloomParts } from "@/components/ui/cellforge-core";11import { useCyclePhase } from "@/components/ui/cellforge-hooks";12import { usePrefersReducedMotion } from "@/components/ui/cellforge-hooks";13import type { CellForgeCommonProps } from "@/components/ui/cellforge-core";1415export type CellAngle20Props = CellForgeCommonProps;1617const MATRIX_SIZE = 7;18const BASE_OPACITY = 0.08;19const HIGH_OPACITY = 0.94;20const CENTER_DIM = 0.2;2122const TRIANGLE_CELLS = new Set([23 "1,3",24 "2,2",25 "2,4",26 "3,1",27 "3,3",28 "3,5",29 "4,0",30 "4,2",31 "4,4",32 "4,6"33]);3435/** Same perimeter ring as CellTriangle1 — center is not on this loop. */36const PERIMETER_PATH: ReadonlyArray<readonly [number, number]> = [37 [1, 3],38 [2, 2],39 [3, 1],40 [4, 0],41 [4, 2],42 [4, 4],43 [4, 6],44 [3, 5],45 [2, 4]46];4748const PATH_LEN = PERIMETER_PATH.length;49const TRAIL_SPAN = 3.35;50const HALF = PATH_LEN / 2;5152function isWithinTriangleMask(row: number, col: number): boolean {53 if (row < 0 || row >= MATRIX_SIZE || col < 0 || col >= MATRIX_SIZE) {54 return false;55 }5657 return TRIANGLE_CELLS.has(`${row},${col}`);58}5960function pathIndex(row: number, col: number): number | null {61 for (let i = 0; i < PATH_LEN; i += 1) {62 const [pr, pc] = PERIMETER_PATH[i]!;63 if (pr === row && pc === col) {64 return i;65 }66 }67 return null;68}6970function modF(n: number, m: number): number {71 return ((n % m) + m) % m;72}7374function behindAlongPath(s: number, i: number, L: number): number {75 return modF(s - i, L);76}7778function smoothstep01(edge0: number, edge1: number, x: number): number {79 if (edge1 <= edge0) {80 return x >= edge1 ? 1 : 0;81 }82 const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));83 return t * t * (3 - 2 * t);84}8586function glowAlongPath(s: number, idx: number | null, L: number): number {87 if (idx === null) {88 return BASE_OPACITY;89 }90 const d = behindAlongPath(s, idx, L);91 const g = 1 - smoothstep01(0, TRAIL_SPAN, d);92 return BASE_OPACITY + g * (HIGH_OPACITY - BASE_OPACITY);93}9495/**96// … truncated
More from @cellforge
All 71 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| All CellForge Loadersall | @cellforge | Components | Free | no deps | |
| Core Spokescell-angle-1 | @cellforge | Components | Free | no deps | |
| Column Rakecell-angle-10 | @cellforge | Components | Free | no deps | |
| Shelf Descentcell-angle-11 | @cellforge | Components | Free | no deps | |
| Skew Driftcell-angle-12 | @cellforge | Components | Free | no deps | |
| Serpent Zipcell-angle-13 | @cellforge | Components | Free | no deps | |
| Pillar Sweepcell-angle-14 | @cellforge | Components | Free | no deps | |
| Tripod Handoffcell-angle-15 | @cellforge | Components | Free | no deps |
