Sliders

Introduction

Sliders are basic elements that allow two types of internal elements: handles and marks. A slider is initialized by a Surface which is used as the track. As such, the slider will automatically inherit the surface's orientation constraints.

Below is an example of a common horizontal slider:

Code:

var surface = new Surface('horizontal');
var slider = new Slider(surface);
slider.trackClassName = 'shaded';
slider.createHandle(Slider.basicHandle('arrow', 'down')).place(0.5);

As you can see, we first create a horizontal surface line (0 height) and designate it as the track when creating the slider. This automatically constrains the slider handle movement to be horizontal.

Vertical Sliders

Similar to horizontal sliders, vertical sliders arise when the slider track is based on a vertical surface.

var surface = new Surface('vertical');
var slider = new Slider(surface);
slider.trackClassName = 'shaded';
slider.createHandle(Slider.basicHandle('arrow', 'left')).place(0.5);

In this case the slider's handle(s) will be constrained to move vertically.

2D Sliders

Finally, sliders can also be 2-dimensional when the track surface is not constrained (a basic area surface).

var surface = new Surface();
var slider = new Slider(surface);
slider.createHandle(Slider.basicHandle('circle', 'uniform')).place(0.5);

Multiple Handles and Handle Styling

Sliders can have multiple handles that are independently controlled.

Below is an example of a common horizontal slider with two separate handles (one pointing up and one pointing down):

Code:

var handle1 = slider.createHandle(Slider.basicHandle('arrow', 'down')).place(0.5);
var handle2 = slider.createHandle(Slider.basicHandle('arrow', 'up')).place(0.8);

Actually, handles are just standard divs that are mounted on a point surface, so you can customize them however you like:

Hey! This can be anything
var customHandle = _('div', { className: 'my-handle'}, [
  _('div', { className: 'handle-tick' }),
  _('div', { className: 'caption-text'}, [_('', 'Hey! This can be anything')])
]);
slider.createHandle(customHandle).place(0.6);

Programmable Constraints

Constraints can be implemented on handles to create all kinds of functionality. This is done by overloading the _place method of a handle:

var discreteTicks = [0.1, 0.3, 0.4, 0.6, 0.9, 1.0];
var handle = slider.createHandle(Slider.basicHandle('arrow', 'down')).place(0.5);
var oldPlace = handle._place;
handle._place = function (x, y) {
  var newX = discreteTicks.reduce(function (acc, cur) {
    return (acc == null || Math.abs(x - cur) < Math.abs(x - acc) ) ?
      cur : acc;
  }, null);
  oldPlace.apply(this, [newX, y]);
}

This is a very old-school way of doing method inheritance since our examples are based on ES5. However, there's nothing stopping you from extending the base SliderHandle class if you opt for a more modern language variant (e.g. TypeScript).

Programmable constraints are a very rich set of tools for controlling sliders. For example, with multiple slider handles, one can write constraints that modify handle behavior relative to the other handles!

The example above is a 2D area-slider with 2 handles. The outer handle is constrained to move within a circle while the inner handle is constrained by the outer handle. Neither of these constraints are built into the slider primitive and yet they can be easily implemented as a programmable constraint! We will leave the implementation details as an exercise to the reader (hint: look at the source!).

Reading handle Value

Values can be read from a handle through its .value() method. Depending on the slider type, it will return a single number or an object containing .x and .y for the corresponding horizontal and vertical values:

-
-
handle.addEventListener('move', function () {
  $('#slider-7-value').innerText = Math.round(this.value() * 1000) / 1000;
});

Remember that the resolution of the value depends on how much area is available to the slider: longer tracks will give you higher resolution for the value.

Static marks and Track Styling

You may have noticed that in examples above, we have set the property .trackClassName on a slider. This is a primitive example of track styling. By default, the track is a div that is assumed to span the entire width and height of the slider's backing Surface and is where handles and marks are placed. You are able to do some basic styling to it, though be aware that setting certain styles (such as width/height) will break a slider's functionality.

Here is what a slider looks like without track styling. Notice that you won't see any track!


        

Remember that the resolution of the value depends on how much area is available to the slider: longer tracks will give you higher resolution for the value.