Dials and timeline
A jog wheel with fine control and inertia, scrub fields you drag sideways, and a compact animation timeline with keys, a loop range and zoom for PixiJS.
Updated pixi-silk 1.0.1View as Markdown
Controls for numbers and time. JogDial and Scrubber are in pixi-silk/ui/dial, and
MiniTimeline is in pixi-silk/ui/timeline. For a rolling number readout, see the odometer on
Interaction and motion.
Turn a value with a jog dial#
import { JogDial } from 'pixi-silk/ui/dial';
const camera = { zoom: 1 };
const undo: number[] = [];
const zoom = new JogDial({
radius: 64,
turn: 2,
step: 0.1,
min: 0.1,
max: 8,
onChange: (v) => (camera.zoom = v),
onCommit: (v) => undo.push(v),
});
Press anywhere on the dial and drag around its centre. The value follows the angle and keeps adding up over full turns, so there is no seam at the top. Past the ring the dial turns slower the further out the pointer is, which gives fine control without letting go.
A fast release keeps the dial spinning until friction stops it. With min and max the ring
becomes a gauge of the range, and past a limit the value stretches and springs back. A tap on the
right half steps up, on the left half down. onChange fires on every snapped change and
onCommit once the dial rests.
| Option | Default | What it does |
|---|---|---|
turn | the range, else 1 | Value change for one full turn near the ring. |
step | 0 | Snap step. 0 turns snapping off. |
fine | 1 | How fast precision grows with distance: the speed is (radius / distance) ** fine. 0 turns it off. |
rubber | 0.12 | How far the value stretches past a limit, as a fraction of a turn. |
inertia | true | Keep spinning after a flick. |
friction | 3.5 | How quickly a flick slows down, about 1 / seconds. |
Drag a number field#
import { Scrubber } from 'pixi-silk/ui/dial';
const size = new Scrubber({ label: 'Size', value: 24, step: 1, min: 1, max: 200, perPixel: 0.5 });
Drag the field sideways to change its value. Shift scrubs at a tenth of the speed
(fineFactor), and Ctrl or Cmd snaps to the step. A tap on either end steps down or up. With
min and max the field fills like a slider. perPixel defaults to a quarter of the step.
Both controls keep their value in state.value, a signal you can also write to move them.
Scrub an animation timeline#
import { MiniTimeline } from 'pixi-silk/ui/timeline';
const timeline = new MiniTimeline({
duration: 120,
fps: 30,
loop: [24, 96],
keys: [
{ id: 'jump', time: 30 },
{ id: 'land', time: 44 },
],
onKeysChange: (keys) => localStorage.setItem('keys', JSON.stringify(keys)),
});
timeline.state.time.subscribe((frame) => console.log('frame', frame));
timeline.state.playing.set(true);
Times are in frames. Scrub the ruler to move the playhead, which snaps to frames and stretches past either end. Drag a key to move it, Shift + click to add keys to the selection, or drag on empty track for a selection box. Selected keys move together and stop at the ends of the track. Drag the green handles on the ruler to set the loop range, and playback wraps inside it.
The wheel zooms around the pointer and Shift + wheel pans. Pixi listens to the wheel passively,
so the page scrolls too unless the canvas fills it. Without a wheel, a double tap on the ruler
zooms in and another zooms back out. zoomBy(), panBy() and showAll() do the same from code.
| State | What it holds |
|---|---|
time | Playhead time in frames. Writing it moves the playhead. |
playing | Writing it starts or stops playback. |
selection | Ids of the selected keys. |
range | Loop range in frames, or null for the whole track. |
view | The visible window in frames. |
| Option | Default | What it does |
|---|---|---|
duration | 120 | Length in frames. |
fps | 30 | Playback speed. |
snap | 1 | Keys and the playhead snap to multiples of this many frames. 0 turns it off. |
minSpan | 8 | Fewest frames the view zooms in to. |
fontSize | 9 | Size of the frame numbers on the ruler. fontFamily and textStyle set the rest. |
label | BitmapText | Builds the frame numbers. Pass silkLabel() from pixi-silk/text for SDF text. |
The timeline holds keys and a playhead, not animation curves. Use state.time to drive your own
tweens or a Spine track.
API reference: JogDial, Scrubber, MiniTimeline