Hue slider

v0.0.10

v0.0.10StableMobStatelessApache-2.0
Installation mix mishka.ui.gen.mob hue_slider
Source View Source

The Mob Hue Slider is the native iOS and Android version of the headless Hue Slider in Mishka Chelekom. It draws a rainbow track and turns a touch or drag on it into a hue from 0 to 360 degrees. Mob renders it with Jetpack Compose on Android and SwiftUI on iOS.

Overview of the Mob Hue Slider component for iOS and Android

The hue slider draws a rainbow on a canvas as a run of filled bands, one band for every two units of width, and lays a marker over the current hue. The track is 300 units wide and 16 units high by default.

The strip is the control, and no native slider sits under it. A touch on the strip sends your screen a {:drag, tag, payload} message with the touch position, and hue_at/2 converts that position to a hue. The phase field reports began on touch-down, dragging while the finger moves and ended on release. A tap therefore sets the hue.

The screen owns the state. The slider draws the value you pass, so you assign the new hue in handle_info/2 and the marker moves. Set label for a caption above the track and show_value for a readout such as 210°. With both, the label sits on the left and the readout on the right.

Generate the hue slider component

Run this from your Mob app's root to add the component to lib/<app>/components/:

bash
mix mishka.ui.gen.mob hue_slider

The generator registers the tag, so you can use it in ~MOB markup. It also copies the shared Color and Event helper modules. The examples on this page use the mishka_ prefix of the showcase app (--module-prefix mishka_), so the tag is <MishkaHueSlider>. Without a prefix, the tag is <HueSlider>.

Hue slider with a drawn spectrum

The rainbow is 150 canvas bands, so the track is a real gradient. This example sets label and show_value, and converts the touch position with hue_at/2 in handle_info/2. The colored box under the slider in the screenshot belongs to the showcase page and is not part of the component.

Hue slider with a drawn spectrum

Hue slider example card titled The spectrum is drawn, not faked, with a 210 degree Hue readout, a rainbow track with a marker and a blue box showing #0080ff
elixir
<MishkaHueSlider value={@hue} label="Hue" show_value={true} on_change={:hue} />

# The strip IS the control, so the event carries a touch POSITION, not a
# value. hue_at/2 inverts the marker, so the colour under the finger is
# the colour selected. Matching {:change, ...} instead renders a perfect
# strip that does nothing.
def handle_info({:drag, :hue, %{x: x}}, socket) do
  {:noreply, Mob.Socket.assign(socket, :hue, MishkaHueSlider.hue_at(x))}
end

Helper functions

The module exports two helpers. Call them from your screen.

FunctionDescription
hue_at(x, width \\ 300)Converts a touch x position, in canvas units from the left edge of the strip, to a hue. It is the inverse of the marker, so the finger and the marker agree. Pass the same width the strip is drawn at.
spectrum(width, height, hue)Returns the draw ops for the rainbow strip with a marker at the given hue, as a list of plain maps.

Props

PropTypeDefaultDescription
valuenumber, 0 to 3600The current hue in degrees. A value outside the range wraps, and 360 stays at the far end of the strip.
widthnumber300Track width in logical units.
heightnumber16Track height in logical units.
labelstringnilCaption above the track.
show_valuebooleanfalseShows a readout such as 210°.
on_changeevent tagNoneSends {:drag, tag, payload} with a touch position. Convert it with hue_at/2.
idstringNoneA native test tag for the strip. A canvas has no text that a test can find it by.

Events

on_change sends {:drag, tag, %{x:, y:, dx:, dy:, phase:}} to the screen that rendered the slider, and the screen handles it in handle_info/2. The payload holds a touch position, so use hue_at/2 to get a hue. The phase field reports began on touch-down, dragging while the finger moves and ended on release. The dx and dy fields in the payload differ between iOS and Android, so build on x and y only.

elixir
def handle_info({:drag, :hue, %{x: x}}, socket) do
  {:noreply, Mob.Socket.assign(socket, :hue, MishkaHueSlider.hue_at(x))}
end

def handle_info(_msg, socket), do: {:noreply, socket}

Differences from the web component

The Mob version leaves these parts of the web slider out or changes them:

  • The track is bands drawn on a canvas, where the web version puts a CSS linear-gradient on the track.
  • on_commit is not ported, because the Mob bridge has no release event.
  • step and large_step (keyboard nudging) are not ported.
  • name and form are not ported. The id prop is a native test tag, not a DOM id.