Sorting and motion
Sorting
order decides which place on screen each row takes.
The easiest way is sortBy:
<Plot … order={sortBy("votes", "desc")}> // largest first
<Plot … order={sortBy("votes")}> // smallest first
<Plot … order={sortBy((d) => d.name)}> // by any value
Rows with equal values keep their places, so ties don’t flicker.
order can also be a list of positions, one per row: [2, 0, 1] puts the first row third.
A position of null hides that row.
import { createSignal } from "solid-js";
import { Chart, Plot, Bar, Label, sortBy } from "@bezda/rhp";
export default function Votes() {
const [votes, setVotes] = createSignal([34, 21, 45, 12, 28]);
const [js, setJs] = createSignal(false);
const [order, setOrder] = createSignal("desc");
const vote = () =>
setVotes(
votes().map((v) =>
Math.max(0, v + Math.round(Math.random() * 20 - 10)),
),
);
return (
<>
<div class="demo-controls">
<button onClick={vote}>New votes</button>
<button
onClick={() => setOrder(order() === "desc" ? "asc" : "desc")}
>
Sort {order() === "desc" ? "up" : "down"}
</button>
<label>
<input
type="checkbox"
checked={js()}
onChange={(e) => setJs(e.target.checked)}
/>{" "}
JS version
</label>
</div>
<Chart scale={[0, 60]} animate={js()}>
<Plot
name={["Maya", "Leo", "Ivy", "Omar", "Zoe"]}
votes={votes()}
order={sortBy("votes", order())}
>
{(d) => (
<div>
<Label edge="start">{d.name}</Label>
<Bar to={d.votes} />
<Label at={d.votes}>{Math.round(d.votes)}</Label>
</div>
)}
</Plot>
</Chart>
</>
);
}How things move
When a value changes, its bar grows or shrinks, and when the order changes, slats slide to their new places. rhp has two ways to do this.
The CSS version is the default. The browser animates each change with CSS transitions: 0.15 s for values, 0.3 s for slats sliding. It costs almost nothing, even with many charts. An Area’s or a Line’s outline moves with a CSS transition too. Safari can’t transition an outline, so there rhp runs that same CSS transition itself.
The JS version counts each number from its old value to its new one, frame by frame, on a single clock shared by the whole page.
Turn it on with animate on the Chart or a Plot.
Everything drawn from a number moves with it, so a label stays exactly at the end of its bar, and two slats cross at the moment their values are equal.
<Chart animate>…</Chart> // the JS version
<Chart animate={{ duration: 400, ease: "ease-in-out" }}>…</Chart> // with your own timing
<Plot animate={["votes"]}>…</Plot> // only some values move
Try both with the checkbox above.
Timing
In the CSS version, set the timing in a slat’s CSS:
.slat { --rhp-length-time: .4s; --rhp-length-ease: ease-in-out; --rhp-slide-time: .5s; }
To time a whole chart at once, set the same variables on the Chart:
<Chart style={{ "--rhp-length-time": ".4s", "--rhp-slide-time": ".5s" }}>…</Chart>
In the JS version, pass duration (ms), ease and slide (ms) to animate:
<Chart animate={{ duration: 400, ease: "ease-in-out", slide: 200 }}>…</Chart>
Any duration works.
Set it to 0 (--rhp-length-time: 0s in the CSS version, duration: 0 in the JS version) and values jump straight to their new places with no motion at all.
Changing data in your own animation frame
rhp writes each change to the chart in the next animation frame, the one that shows it.
A change you make inside your own requestAnimationFrame callback can come after rhp’s in that frame, and then shows a frame late.
Make it inside drawing() and it reaches the chart in the same frame:
import { drawing } from "@bezda/rhp";
requestAnimationFrame(() => drawing(() => setValue(next)));
Reduced motion
If someone has asked their system for less motion, rhp jumps to new values instead of animating them.
Other ways to reorder
reorder on a Plot picks how slats change places:
"slide"(the default): slats slide on screen."move": the slats move in the page instead of sliding, so the page’s reading order follows the sort."refill": slats stay put and swap contents.