Shiny Knob Control with jQuery and CSS3

Shiny Knob Control with jQuery and CSS3

In this tutorial we will be writing a jQuery plugin for creating a shiny knob control. Aptly named knobKnob, this plugin will use CSS3 transformations and jQuery’s new event handling methods to give visitors of your website a new way of interactively choosing a value from a range.

KnobKnob is also on Github.

Update: Thanks to ranyefet the plugin now works on mobile devices [changes].

The HTML

The HTML markup for the page is rather straightforward. We are only going to need a placeholder element for the control – the rest is going to be dynamically generated by the plugin. Just in case, here is the complete markup of the page:

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Shiny Switches with CSS3 &amp; jQuery | Tutorialzine Demo</title>

        <!-- CSS stylesheets -->
        <link rel="stylesheet" href="assets/css/styles.css" />
        <link rel="stylesheet" href="assets/knobKnob/knobKnob.css" />

        <!--[if lt IE 9]>
          <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
        <![endif]-->
    </head>

    <body>

		<section id="main">

			<div id="bars">
				<div id="control">
					<!-- The knob markup will go here -->
				</div>
                                <!-- The colorful dividers will go here -->
			</div>

		</section>

        <!-- JavaScript includes -->
		<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
		<script src="assets/knobKnob/transform.js"></script>
		<script src="assets/knobKnob/knobKnob.jquery.js"></script>
		<script src="assets/js/script.js"></script>

    </body>
</html>

First we include the latest version of jQuery, transform.js which levels cross-browser support of the CSS3 transform properties we will be using, the knobKnob plugin file and script.js, which pulls everything together.

The #control div is where the plugin markup will be generated. Below we will insert divs that will become the colorful bars around it. They are not part of the KnobKnob plugin, we will be showing them depending on the chosen value in the control. KnobKnob also comes with a stylesheet that determines the looks of the knob. You can see it included in the head section.

Now lets write this plugin!

Shiny Knob Control

Shiny Knob Control

The jQuery Code

You can find the plugin source files in the knobKnob folder. To use it in your project simply unzip it in your website’s assets folder and include the files you find inside. Here is the actual plugin file:

assets/knobKnob/knobKnob.jquery.js

/**
 * @name		jQuery KnobKnob plugin
 * @author		Martin Angelov
 * @version 	1.0
 * @url			http://tutorialzine.com/2011/11/pretty-switches-css3-jquery/
 * @license		MIT License
 */

(function($){

	$.fn.knobKnob = function(props){

		var options = $.extend({
			snap: 0,
			value: 0,
			turn: function(){}
		}, props || {});

		var tpl = '<div class="knob">\
				<div class="top"></div>\
				<div class="base"></div>\
			</div>';

		return this.each(function(){

			var el = $(this);
			el.append(tpl);

			var knob = $('.knob',el)
				knobTop = knob.find('.top'),
				startDeg = -1,
				currentDeg = 0,
				rotation = 0,
				lastDeg = 0,
				doc = $(document);

			if(options.value > 0 && options.value <= 359){
				rotation = currentDeg = options.value;
				knobTop.css('transform','rotate('+(currentDeg)+'deg)');
				options.turn(currentDeg/359);
			}

			knob.on('mousedown', function(e){

				e.preventDefault();

				var offset = knob.offset();
				var center = {
					y : offset.top + knob.height()/2,
					x: offset.left + knob.width()/2
				};

				var a, b, deg, tmp,
					rad2deg = 180/Math.PI;

				knob.on('mousemove.rem',function(e){

					a = center.y - e.pageY;
					b = center.x - e.pageX;
					deg = Math.atan2(a,b)*rad2deg;

					// we have to make sure that negative
					// angles are turned into positive:
					if(deg<0){
						deg = 360 + deg;
					}

					// Save the starting position of the drag
					if(startDeg == -1){
						startDeg = deg;
					}

					// Calculating the current rotation
					tmp = Math.floor((deg-startDeg) + rotation);

					// Making sure the current rotation
					// stays between 0 and 359
					if(tmp < 0){
						tmp = 360 + tmp;
					}
					else if(tmp > 359){
						tmp = tmp % 360;
					}

					// Snapping in the off position:
					if(options.snap && tmp < options.snap){
						tmp = 0;
					}

					// This would suggest we are at an end position;
					// we need to block further rotation.
					if(Math.abs(tmp - lastDeg) > 180){
						return false;
					}

					currentDeg = tmp;
					lastDeg = tmp;

					knobTop.css('transform','rotate('+(currentDeg)+'deg)');
					options.turn(currentDeg/359);
				});

				doc.on('mouseup.rem',function(){
					knob.off('.rem');
					doc.off('.rem');

					// Saving the current rotation
					rotation = currentDeg;

					// Marking the starting degree as invalid
					startDeg = -1;
				});

			});
		});
	};

})(jQuery);

The plugin takes a number of options as a parameter object – snap, value and turn:

  • snap is a number of degrees that are snapped to zero. You can test this by slowly turning the knob down;
  • value is the initial rotation of the knob (also in degrees);
  • turn is a callback function that is called every time the knob is turned. Its only argument is a ratio (from 0 to 1) of the rotation. We will use this function in a moment to determine how many of the colorful dividers to show.

In the code above you can see that we are using the Math.atan2 function (as we did in the PointPoint plugin) to calculate the angle (in radians) between the mouse pointer and the center of the knob. By keeping track of the angle in the start and end position of the drag, we can determine how much to rotate the knob.

Later we are also using jQuery 1.7′s new methods for manipulating event listeners – on and off.

Now lets see how we can use this plugin.

assets/js/script.js

$(function(){

	var colors = [
		'26e000','2fe300','37e700','45ea00','51ef00',
		'61f800','6bfb00','77ff02','80ff05','8cff09',
		'93ff0b','9eff09','a9ff07','c2ff03','d7ff07',
		'f2ff0a','fff30a','ffdc09','ffce0a','ffc30a',
		'ffb509','ffa808','ff9908','ff8607','ff7005',
		'ff5f04','ff4f03','f83a00','ee2b00','e52000'
	];

	var rad2deg = 180/Math.PI;
	var deg = 0;
	var bars = $('#bars');

	for(var i=0;i<colors.length;i++){

		deg = i*12;

		// Create the colorbars

		$('<div class="colorBar">').css({
			backgroundColor: '#'+colors[i],
			transform:'rotate('+deg+'deg)',
			top: -Math.sin(deg/rad2deg)*80+100,
			left: Math.cos((180 - deg)/rad2deg)*80+100,
		}).appendTo(bars);
	}

	var colorBars = bars.find('.colorBar');
	var numBars = 0, lastNum = -1;

	$('#control').knobKnob({
		snap : 10,
		value: 154,
		turn : function(ratio){
			numBars = Math.round(colorBars.length*ratio);

			// Update the dom only when the number of active bars
			// changes, instead of on every move

			if(numBars == lastNum){
				return false;
			}
			lastNum = numBars;

			colorBars.removeClass('active').slice(0, numBars).addClass('active');
		}
	});

});

The colorful bars that are displayed around the knob are not part of the plugin. And they shouldn’t be – the plugin only handles the control itself which makes it easier to reuse it.

The code above creates a set of 30 divs with colors gradually going from green to red. These are then rotated by 12 degree increments. Thanks to the turn callback function passed to the plugin, this code can determine how many of the colorful bars to show. You can see the rest of the bar styling in assets/css/styles.css.

We are done!

With this our plugin is complete! You can use it as part of control panels and other administrative pages, everywhere you want to give users the ability to choose from a pool of values. Next time we will use this plugin to enhance the new range form element.

Join our newsletter and get our PSDs!19,517 people learn about HTML5, JS and more. Join them!

by Martin Angelov

Martin is a web developer with an eye for design from Bulgaria. He founded Tutorialzine in 2009 and it still is his favorite side project.

40 Comments

  1. Jacob Dubail says:

    Why not use some type of semantic form element for this? Maybe a range element? Something that degrades nicely, maybe?

    Cool effect, though!

    1. Martin Angelov says:

      Yep, that is the idea. However I decided to split the tutorial in two parts. The plugin is discussed now, and next time we'll have a play with the range element.

  2. Alex Forey says:

    Thanks for the tutorial. Now the problem is to find an excuse to use these on my site...

  3. josh says:

    The lighting on the knob shouldn't rotate.

    1. Martin Angelov says:

      You are right, but it would be boring with only the small pointer moving.

  4. J.F. Herrera says:

    Great tutorial! Just this week I was thinking adding a similar effect to a current project. You just saved me tons of work.

  5. Sumair says:

    cool work

    Put cursor pointer so that we know where to grab the button

  6. Farid says:

    Very nice plugin!) But why you did't make rotate animation more smoothly? ) Like
    .stop().animate({....},250, 'easeInQuart');
    I think, it will be better)

    1. Martin Angelov says:

      It doesn't use jQuery animations. It is all updated when you move your mouse.

  7. Erik says:

    If mouse out of knob when rotating, it doesn't rotate (

  8. really useful for me ! Thanks for this tutorial :)

  9. Junior says:

    As I can use it for an audio player. I use have given? helpme

  10. Sanchit says:

    Nice use of css3 transform and your code is very interesting its help me to do a work on more concepts

  11. Nathan B says:

    I think the idea is great, and I was looking at something like this, however is there anyway you can change it so you can drag up and down, if you've ever used the DJing software called Traktor, then you will see it's quite a big usability factor!

    Hope you can!

    Thanks

    1. Martin Angelov says:

      You mean to increase/decrease the control by moving your mouse up and down? It is actually going to be easier, as you don't need to calculate angles and rotations.

      1. Nathan B says:

        Yup that's right, but it would have to go out of the radius of the knob, e.g.
        ^
        |
        |
        O
        |
        |
        v

        If that makes sense?

  12. e11world says:

    Very nice and I didn't actually know you could do that with jQuery yet! Love it!

  13. Pol says:

    Impressive how nice works on iOS yet I can't integrate it to a current project I'm working on.

    Maybe I'm missing something but how can I retrieve the value of current degree so that I can place three "icons" and let users choose using the knob one of the three "options".

    Or just the basics, how can I tweek the "turn" callback to print to an the current value ?

    Thanks for all the nice tuts you put out there.
    p

    1. Martin Angelov says:

      Be sure to fetch the latest version of the plugin from github. After this, you can use the ratio that is passed to the turn callback function as you would Math.rand():

      // Will be either 0, 1 or 2:
      var chosenIcon = Math.floor(turn*2); 
      
  14. alee says:

    Guys can you please post some examples where you're using this knob? I'd like to take some ideas. Thanks :)

  15. Love it. Sleek and smooth.

  16. Joe says:

    I do a lot of audio recording and design, and so many of the plugins are plagued with fake knobs and dials, and they all have one thing in common. They are terrible, terrible interfaces for mouse based control. Interface designers: stop it! No more.

  17. Patrick says:

    Trying to use these on a site we are working on, and they work fine in standard browser, but when I checked on my iphone, I can't turn the dials :(... maybe resizing them messed something up..???
    http://www.kingsfoodservice.com/SHAWNEE-SERVICES%20SITE/home.html

    1. Patrick says:

      I just saw the update above. Once we add that it should work. On the site I posted earlier the dials will be the nav bar obviously, the page you are on will load to 95% all others to 50%, with page load on click release. Still really rough with alot of place holders and only the one page, but should be much closer by the end of the week.

  18. co2/h20 says:

    How to use the knob to control system volume?

  19. Dhanai says:

    Possible to have the knob auto rotate endlessly? And then stop when moused down to manually control?

  20. cookie says:

    How would I change the background for multiple knobs on each page? Looks like the .knob and the .top class divs are being appended via js and the css background image knob.png is applied to .knob ,top

  21. keith says:

    it's a pity the knob doesnt load properly in SAFARI...anyone found any solution?

  22. Colin says:

    great, but, not very usable !

    it isn't necessarily the best solution to copy something directly from real life on a computer.

    try making it so a straight up and down motion rotates the knob entirely rather than having to trace it in a circle ... this is how programs like Propellerheads Reason solve the same problem.

  23. Torres says:

    Thks so much ! it's very helpful

  24. matt garner says:

    Turn it up to 11 :)
    cool stuff. now pandora just needs to use this...

  25. Sujay says:

    Hi, excellent work, thanks a lot.
    I need to have more than one knob, can u help me how can i have more knobs.

  26. Daniel says:

    Hi, great script.

    Any guides, ideas or tips on how to have two knobs, one big on the back, and a smaller on the front, and that both knobs can rotate?

    Thanks

  27. Sujay says:

    Hi,
    I posted a question, but i didn't got the reply, i think we cannot have more than one knob using this code.

  28. Magister says:

    How to have the knob working with STEPS as : knotRot.js,
    see http://www.domitable.com/static/side-projects/jquery-plugins/knobRot/demo.html

    Btw, is a really cool plugin.

    Thanks to help me.

  29. Josh says:

    You can add a minVal and maxVal to the options to limit the range of motion. Also, I thought it would be better to use the snap parameter to make the knob snap to an interval - so snap:90 would make the knob snap to the 4 cardinal directions (N, E, S, W)

    http://positlabs.com/files/code/js/SnapKnob/assets/knobKnob/knobKnob.jquery.js

  30. Very Cool! I will definitely use on future projects!

  31. Saif says:

    Hey,

    Excellent code. :)
    I guess i have not used jquery and CSS3 at the same time before, :)
    Thanks

    Saif

Add Comment

Add a Reply

HTML is escaped automatically. Surround code blocks with <pre></pre> for readability.
Perks:   **bold**   __italics__   [some text](http://example.com) for links