niloy / blog

blog raw files
3 stars 1 forks source link

The Knob component #15

Open niloy opened 10 years ago

niloy commented 10 years ago

Creating the Knob component

The working example can be seen here

The first step was to find a good mockup for the Knob. I found it here: UIParade. I could not find the psd for the image, so I just copied the image.

The next step was to remove the arrow on the image, since the position of the arrow depends on the value of the knob. So I opened the image in Gimp and used the clone tool to remove the arrow. In the component, am creating the arrow using pure css. Also, there is a lot of empty space on the left and right side of the knob image, so I croped it and made it smaller. The final image looked like this:

Next, I need to put this in a html page. So the basic strucure I decided to use is 3 div stacked on top of each other. The bottom most div will have the knob image as background-image. The middle div will be a transparent div with just the arrow. And a third empty div which will simply server the purpose of receiving input(mouse and wheel).

-------------        -------------      -------------  
|           |        |           |      |           |  
|           |        |           |      |           |  
|           |        |         ->|      |           |  
|           |        |           |      |           |  
|           |        |           |      |           |  
-------------        -------------      ------------- 
   Div 1                  Div 2              Div 3
 (Knob Image)       (Contains arrow)     (Input reciever)

Now, when you want to change the position of the arrow, you simply use css transform: rotate on div2. I decided that the knob component will always return value in the range 0 - 1, just like Math.random. This reduces calculation complexity of the component and is still generic enough to be used with any number range.

If you look at the image, at the max, you can turn the knob to about 270 degrees. This means, any value between 0-1 needs to be mapped to 0-270 degrees. The formula is simple:

degrees = value * 270

So, say the value is 0.5, we put transform: rotate(135deg) css on Div2. To make the rotation a smooth animation, we simply add transition: transform 0.25s css on it. These steps happen when we set the value of the knob programatically. The knob value setter implements this code. The code looks like this:

var deg = this.value * this.MAX_KNOB_ANGLE;
this.arrowContainer.style.transform = "rotate(" + deg + "deg)";

Now comes the tricky part, when user changes values of the knob using mouse. The user will hold the arrow and drag it in a circlular motion. As it turns out, this is really very simple with some basic geometry. This is where Div3 comes into the picture. I have setup the mousedown, mousemove and mouseup on div3. Whenever the user clicks and drags the mouse, we simply need to measure the angle the mouse makes with the x-axis. The x-axis is at the center of div3. Refer the image below:

knob

The angle (theta) is calculate as:

angle = Math.atan2(mousey - centery, mousex - centerx)

Once we get the angle, converting it to value is trivial using the formula we described earlier.

value = angle / 270

Once we get the value, we simple call the knob value setter which in turn rotates div2. This completes the illusion of rotating the Knob.

The entire code can be see here.