Wheel
<o-wheel> is a prize wheel: it spins, slows down and lands on a result picked by weight.
The segments are <o-arc>s with a value, so Orbit stacks them around one ring, each as wide as its weight. The turning part is a universe inside the universe: a nested .bigbang that the Web Animations API rotates, so Orbit has nothing to redraw during a spin. The pointer and the button stay in the outer system. Each label is a satellite on its own ring, with --o-from at the middle of its segment.
The result is picked first, fairly by weight with crypto.getRandomValues, and the wheel then turns a few times and slows down onto it with a long ease-out. Every time a peg passes, the pointer flicks and a tick event fires. When it stops, labels on the left half turn over to read upright.
Attributes
| Attribute | Default | What it does |
|---|---|---|
items | [] | JSON: strings, or { label, color?, weight?, value? }. weight (default 1) sets both the size of the segment and its odds; 0 leaves an item out. Up to 100 items. |
labels | across | across writes labels from the middle out, along writes them along the rim, none hides them. Long labels are shortened with an ellipsis. |
duration | 5 | Length of a spin, in seconds (4, 4s or 4000ms). |
turns | 5 | Full turns before the wheel settles. |
remove-winner | off | For raffles: the winner stays highlighted, then leaves the wheel when the next spin starts. |
controls | on | controls="false" hides the Spin button. The wheel itself then becomes the button. |
label | Spin | Text of the button. |
pegs | up to 36 items | Dots on the rim at each boundary. pegs="false" hides them. |
gap | 0.4 | Space between segments, in percent of the widget width. |
disabled | off | No spins from the button, the pointer, keys or spin(). |
size | 300px | Width of the widget. |
theme | dark, light or glass. Also inherited from an ok-theme-* class on a container. See Shared options. |
Without a color, items take the six accents of the demos in turn, never the same color twice in a row. Label text is dark or white, whichever reads better on its segment.
Properties, methods and events
| Name | What it does |
|---|---|
spin(index?) | Spins and returns a promise with the result. Without an index the result is random, weighted; with one, the wheel lands on that item. While spinning it returns the same promise. It resolves with null when the wheel is empty or disabled. |
items | The items, as given. Setting it writes the attribute. |
spinning | true during a spin. |
result | The last result, or null. |
spin | A spin started. event.detail.duration is its length in seconds. |
tick | A boundary passed the pointer. event.detail has the index and label now under it. Good for a click sound. |
result | The wheel stopped. event.detail has index, label, value and item. |
wheel.addEventListener('result', (e) => claim(e.detail.value))
const { label } = await wheel.spin()
Styling
| Property | Default | What it does |
|---|---|---|
--ok-wheel-rim | --ok-bezel, #232b35 | Rim, lines between segments and the button's ring. |
--ok-wheel-pointer | --ok-hand, #e8edf2 | The pointer. |
--ok-wheel-peg | --ok-hand, #e8edf2 | Pegs on the rim. |
--ok-wheel-hub | --ok-surface, #161b22 | The button. |
--ok-wheel-hub-text | --ok-text, #e8edf2 | Text of the button. |
--ok-focus | #3da9fc | Focus ring. |
--ok-font-size | 8cqmin | Base font size of the button, as a share of the widget. |
During a spin the element has the class is-spinning; after one, has-result, and the winning segment is-winner while the others fade.
More examples
Labels along the rim, and weights
The jackpot is half as wide as the others, so it comes up half as often.
A raffle
With remove-winner every name can win once. Setting items again starts over.
A result decided elsewhere
When the outcome comes from a server, ask it first and pass the index to spin(). Here the buttons stand in for the server, and the wheel is the button itself.
Accessibility
The wheel is a role="group" named after its items, with a real button in the middle. Enter or Space on the button spins it. With controls="false" the wheel itself is a role="button" in the tab order, and Enter or Space spin it. During a spin the button is aria-disabled and the wheel aria-busy; the result is announced through a polite live region, as “Result: Pizza”.
With reduced motion the spin takes under a second, without extra turns or the flicking pointer. The result event and the announcement are the same.