The funky slider

Balance is a central concept in this project. The team has been designing the interface for the platform so that this concept is translated into graphics and interaction. An example of that is what we’re calling ‘the funky slider’ – for lack of a better name. It is nothing more than a range input which rotates when the knob moves – an idea that’s simple as a concept and in execution but nevertheless strong enough to cause impact.

The intended outcome looks more or less like this:

The idea is to use the range input for navigation purposes and tilt it so that it is displayed in a different angle at every step. If we imagine that users will drag the knob to see the next or previous photo, we have to take into consideration that the knob cannot ‘jump’ in and out of the line. Neither should it move away from the mouse/finger – it should stay in place while the line rotates around it. In other words, we have to make sure that the knob is always in the center of the rotation.

This is a range input.

According to W3C, it “represents an imprecise control for setting the element’s value to a string representing a number”. If your browser supports it, you should be able to see it on web pages.

A range input has the following attributes: step, min, max and value. The value is represented by the knob position, in relation to the size (width) of the input. Step refers to the interval between values. The default step value is 1. If the step were 2 instead, you’d see 5 before 7, not 6. Min and max are self-explanatory. 😉

Understanding CSS transform: rotate(deg)

To rotate an element with CSS we use the transform property combined with the rotate( ) transform function. As in:

transform: rotate(xdeg);

In the case of our input element, we could write something like:

transform: rotate(15deg);

This would rotate our input 15 degrees:

The transform-origin property

According to MDN, “The rotate( ) CSS function defines a transformation that moves the element around a fixed point (as specified by the transform-origin property) without deforming it. The amount of movement is defined by the specified angle; if positive, the movement will be clockwise, if negative, it will be counter-clockwise.”

That means that the rotation amount is defined by the angle – the number indicated inside the parenthesis. And the fixed point around which the rotation occurs is defined by the transform-origin property. Well, when transform-origin is not specified, the default is used: 50% 50% (x-offset y-offset). By default, the center of the element is the center of the transformation – in our case, the rotation.

Here’s a visual comparison between two instances of the same element, both rotated 15 degrees. While the input on the left was assigned the default value 50% 50% for the transform-origin property, the input on the right was assigned 100% 0% (right edge):

To recap: what we want to accomplish is the ability to rotate the input element using different angles along the line and changing the value of transform-origin so that it matches the position of the knob. When the knob is all the way to the left (0% of the line width), we want the input to rotate, say, -4 degrees. When the knob position corresponds to 25% of the line width, we want the input to rotate, say, -12 degrees using the knob position as the transform-origin. When the knob is in the middle of the line, we want the input to rotate, say, 0 degrees and use the knob position as center of the rotation. We’ll also define rotation values for the positions 75% and 100%. Our rotation values could be illustrated as follows:

0% 25% 50% 75% 100%
-4 -12 0 2 4

Of course, we would like the rotation values to be interpolated between those points. For calculating that, we used a very handy D3 function: scale( ). “Scales are functions that map from an input domain to an output range.” (Mike Bostock, creator of D3.js among other projects)

var scale = d3.scale.linear()
.domain([0, 25, 50, 75, 100])
.range([-4, -12, 0, 2, 4]);

The next step was to calculate the position of the knob and use that value for the transform-origin property, assigning a new value every time that the knob moves. Here’s how we did it. You can jump straight to the fiddle if you prefer.

//declaring the variables we'll be using

var thumbPos, origin, originPerc, degToRotate, minVal, maxVal, curVal, sliderWidth;

//defining our scale

var scale = d3.scale.linear()
.domain([0, 25, 50, 75, 100])
.range([-4, -12, 0, 2, 4]);
//play with these values (keeping the same amount of items as in domain([]) )

//this function will be called every time we want to change the rotation and transform-origin values
//it needs one parameter, which is the width of the input

function setRotation(w) {
//slider width should be considered as passed to the function
sliderWidth = w;
minVal = $('#slider').prop('min');
maxVal = $('#slider').prop('max');
//current input value
curVal = $('#slider').val();
//knob position, relative to the input width
thumbPos = ((curVal - minVal / maxVal - minVal) / maxVal) * sliderWidth;
//add half of the knob width to find the center of the knob
origin = thumbPos + 10;
//value above, in percentage
originPerc = origin / sliderWidth * 100;
//use the scale function to determine how many degrees to rotate at a given position
degToRotate = scale(originPerc);

//use the two last values to change the input rotation and transform-origin property
'transform': 'rotate(' + degToRotate + 'deg)',
'transform-origin': originPerc + '% 0%'

//output – illustrative purposes only
$('output').html('Rotation in degrees ' + degToRotate + '
Rotation center (knob position) ' + thumbPos + '
Step ' + curVal + ' of ' + maxVal + ' (' + curVal / maxVal * 100 + '%)');

//initial rotation angle
setRotation($('#slider').width()); //comment this out if you want the input NOT to be rotated when the page loads

//call to setRotation function when the input changes
$("#slider").on("input change", function() {

In this fiddle you can see it in action and experiment* with some values.

*All you need to do is edit rotation values in the Javascript tab (line 7) and click Run

Tags: , , , ,