Large collections and motion
The mark cap
Section titled “The mark cap”At most about maxMarks marks are drawn (600 by default). Above that, neighbouring items that agree
on every encoded dimension share a mark, layout.itemsPerMark says how many, and the description
says so too. Nothing is sampled away: every item is in exactly one mark, and a mark never mixes
categories.
Try 30,000 books below. The layout stays fast, and the print looks like the 600-book one, because it is drawn with the same number of marks.
large-collections.tsximport { useMemo, useState } from "react";import type { RidgeStyle } from "@data-profile/fingerprint";import { FingerprintCanvas, FingerprintViewSwitch, useFingerprint, type FingerprintView,} from "@data-profile/fingerprint/react";import "@data-profile/fingerprint/styles.css";import { dimensions, library } from "./library";
const views: FingerprintView[] = [ { id: "genre", label: "Genre", encoding: { arrange: "genre" } }, { id: "decade", label: "Published", encoding: { arrange: "decade" } },];const counts = [12, 120, 600, 3000, 30000];
export default function LargeCollections() { const [count, setCount] = useState(600); const [maxMarks, setMaxMarks] = useState(600); const [ridges, setRidges] = useState<NonNullable<RidgeStyle["show"]>>("auto"); const items = useMemo(() => library(count), [count]); const fp = useFingerprint({ items, dimensions, views, maxMarks, unit: "books" }); return ( <div> <div className="demo-toolbar"> <label> Books{" "} <select value={count} onChange={(event) => setCount(Number(event.target.value))}> {counts.map((value) => ( <option key={value} value={value}> {value.toLocaleString()} </option> ))} </select> </label> <label> maxMarks{" "} <select value={maxMarks} onChange={(event) => setMaxMarks(Number(event.target.value))}> {[200, 600, 1500].map((value) => ( <option key={value}>{value}</option> ))} </select> </label> <label> Ridges{" "} <select value={ridges} onChange={(event) => setRidges(event.target.value as typeof ridges)} > {["auto", "always", "never"].map((value) => ( <option key={value}>{value}</option> ))} </select> </label> <FingerprintViewSwitch views={fp.views} value={fp.viewId} onChange={fp.setViewId} /> </div> <FingerprintCanvas {...fp.canvasProps} title="A library" ridges={{ show: ridges }} /> <p className="demo-note"> {fp.layout.nodes.length.toLocaleString()} marks for {fp.layout.itemCount.toLocaleString()}{" "} books {fp.layout.itemsPerMark > 1 && `: one mark per ${fp.layout.itemsPerMark} books`}. </p> </div> );}Motion
Section titled “Motion”Marks glide to their new places between views while there are at most motion.glide of them (800)
and recolour gently up to motion.fade (400). Above those they change at once, because SVG cannot
animate more marks smoothly. Under prefers-reduced-motion nothing animates. Pass
motion={{ glide: 0, fade: 0 }} to turn it off.
Ridges
Section titled “Ridges”The faint ridges show while a collection is too small for its marks alone to read as a fingerprint
(show: "auto", fading from 60 items to 160), and ridges={{ show: "always" }} or "never"
overrides that. Their opacity, color and width are yours to set too.