Sections and arcs
A ring is a list of sections laid out clockwise from startAngle (default -90, twelve o’clock). Each section’s arc is its weight share of 360 degrees.
.startAngle(-90)
.sections([
{ id: 'mini', label: 'MINI', value: 'mini', weight: 5 },
{ id: 'grand', label: 'GRAND', value: 'grand', weight: 0.5 },
])
Ids, labels, values#
idnames the section insetResult({ section }), in events and in the debug output. Unique per ring.labelis what a label-drawing skin writes. Defaults to the id;''draws nothing.valueis the payload the section stands for. Several sections may share one, andsetResult({ value })lands on one of them.tagsare yours. Skins on this site use them to pick authored plates.
Weights#
Relative, positive, finite. weight: 0.5 is half a default section. Change them at run time with wheel.setWeights(); see Dynamic sections.
Styles#
Per-section overrides for the painted skin and for labels on any skin:
style: {
fill: 0xf1c40f, alpha: 1, stroke: 0xffffff,
labelColor: 0x2b1d00, labelSize: 30, labelFont: 'Roboto Condensed', labelWeight: '800',
labelOrientation: 'radial' | 'tangential' | 'upright',
labelRadius: 0.68, // fraction of the outer radius
}
Sections without a fill cycle the ring’s palette (.palette([...])).
Reading the geometry#
const s = wheel.geometry.byId('grand'); // { startAngle, endAngle, midAngle, arc, ... }
wheel.geometry.sectionAt(localAngle); // which section covers a wheel-local angle
wheel.sectionUnderPointer(); // right now
wheel.main.localAngleUnderPointer(); // wheel-local degrees under the pointer
Angles are wheel-local: they turn with the disc. A point at local angle a is on screen at a + rotation, so the pointer at screen angle p reads local p - rotation. A clockwise spin therefore sweeps decreasing local angles under the pointer: it enters a section through its endAngle and leaves through its startAngle. geometry.entryAngle(section, direction) spells this out; the anticipation planner relies on it.
Rich labels#
A label is not only a string. Give a section content: any container, or a factory that builds one. The label layer of every label-drawing skin (GraphicsRingSkin, TextureRingSkin with labels: true, SpineRingSkin with labels: true, the studio skins on this site) places it at the label radius, rotates it by labelOrientation and fits it into the section’s room.
import { Sprite, Text, Container } from 'pixi.js';
.sections([
// A ready-made object.
{ id: 'x2', value: 2, content: new Sprite(coinTexture) },
// A factory: built once, from the section and the room it has.
{ id: 'jackpot', weight: 2, content: (ctx) => {
const view = new Container();
const icon = new Sprite(crownTexture);
const text = new Text({ text: 'JACKPOT', style: { fontSize: 40, fontWeight: '800', fill: 0xffd23f } });
text.anchor.set(0.5); text.position.y = icon.height / 2 + 24;
view.addChild(icon, text);
ctx.fit(view, { padding: 0.1 }); // scale into the slot, 10% free around
return view;
},
style: { labelOrientation: 'tangential', labelRadius: 0.62 } },
// A Spine instance works the same way: it keeps animating after placement.
{ id: 'bonus', content: () => Spine.from({ skeleton: 'bonusSkeleton', atlas: 'fxAtlas' }) },
])
The factory receives a LabelContext: the resolved section, outerRadius, innerRadius, the slot (see below) and a bound fit(). Content is built once per section and rebuilt only when the section is given a different content. toConfig() keeps the text label and drops content; rich labels are code.
Orientation applies to content as to text. 'tangential' puts the top of the content toward the rim (upright at twelve o’clock); 'tangential-in' toward the hub (upright at six o’clock, for a wheel read from below, like the Pragmatic recipe); 'upright' counter-rotates every frame.
Fitting content#
The room a section offers is a slot: the chord across the wedge at the label radius, and the radial room around that point inside the ring band. labelSlot() computes it; the label layer fits every label into it with labelFit ('contain' by default, also 'cover', 'width', 'height', 'none').
import { labelSlot, scaleToFit, fitContainer, fitText } from 'pixi-wheels';
const slot = labelSlot(section, outerRadius, innerRadius, { radius: 0.68, orientation: 'radial' });
// slot.width / slot.height: the box, oriented like the label. slot.chord, slot.radial: the raw room.
fitContainer(view, slot); // sets view.scale so its local bounds fit; returns the factor
fitContainer(view, slot, { mode: 'width', max: Infinity }); // match the width, allowed to grow
fitText(text, { width: slot.width * 0.8, height: slot.height * 0.8 }); // lowers fontSize instead: crisp glyphs
scaleToFit({ width: 400, height: 80 }, slot); // the pure number, no PixiJS
Content is re-fitted whenever the geometry changes, so dynamic sections shrink and grow their labels with their arcs. Scaling never enlarges by default (max: 1): author content at the size it should have on a full-size wedge and let the fit only take it down.