Fluid Map
dziedzorm/fluid-mapFreeComponent
A dotted world map with clockwise swirl physics on mouse interaction. Dots animate with spring-damping and settle like dust.
Install it
npx shadcn@latest add https://raw.githubusercontent.com/kennyAnyi9/dziedzorm/main/public/r/fluid-map.jsonSource
1"use client";23import * as React from "react";4import { createMap } from "svg-dotted-map";5import { clsx, type ClassValue } from "clsx";6import { twMerge } from "tailwind-merge";78function cn(...inputs: ClassValue[]) {9 return twMerge(clsx(inputs));10}1112export interface Marker {13 lat: number;14 lng: number;15 size?: number;16 pulse?: boolean;17}1819type MapMarker<M extends Marker> = Omit<M, "lat" | "lng"> & {20 x: number;21 y: number;22};2324export interface FluidMapProps<25 M extends Marker = Marker,26> extends React.SVGProps<SVGSVGElement> {27 width?: number;28 height?: number;29 mapSamples?: number;30 markers?: M[];31 dotColor?: string;32 markerColor?: string;33 dotRadius?: number;34 stagger?: boolean;35 pulse?: boolean;36 /** Enable swirl physics on mouse interaction */37 fluid?: boolean;38 /** Radius of the influence area around the cursor (in SVG units) */39 fluidRadius?: number;40 /** Strength of the swirl force */41 fluidStrength?: number;42 renderMarkerOverlay?: (args: {43 marker: MapMarker<M>;44 index: number;45 x: number;46 y: number;47 r: number;48 }) => React.ReactNode;49}5051export function FluidMap<M extends Marker = Marker>({52 width = 150,53 height = 75,54 mapSamples = 5000,55 markers = [],56 dotColor = "currentColor",57 markerColor = "#FF6900",58 dotRadius = 0.2,59 stagger = true,60 pulse = false,61 fluid = true,62 fluidRadius = 20,63 fluidStrength = 0.4,64 renderMarkerOverlay,65 className,66 style,67 ...svgProps68}: FluidMapProps<M>) {69 const svgRef = React.useRef<SVGSVGElement>(null);70 const dotsGroupRef = React.useRef<SVGGElement>(null);7172 const { points, processedMarkers, xStep, yToRowIndex } = React.useMemo(() => {73 const { points: pts, addMarkers } = createMap({ width, height, mapSamples });74 const pm = addMarkers(markers);7576 const sorted = [...pts].sort((a, b) => a.y - b.y || a.x - b.x);77 const rowMap = new Map<number, number>();78 let step = 0;79 let prevY = Number.NaN;80 let prevXInRow = Number.NaN;8182 for (const p of sorted) {83 if (p.y !== prevY) {84 prevY = p.y;85 prevXInRow = Number.NaN;86 if (!rowMap.has(p.y)) rowMap.set(p.y, rowMap.size);87 }88 if (!Number.isNaN(prevXInRow)) {89 const delta = p.x - prevXInRow;90 if (delta > 0) step = step === 0 ? delta : Math.min(step, delta);91 }92 prevXInRow = p.x;93 }9495 return { points: pts, processedMarkers: pm, xStep: step || 1, yToRowIndex: rowMap };96 }, [width, height, mapSamples, markers]);9798// … truncated
What it pulls in
npm packages
