Shiny Knob Control with jQuery and CSS3

Demo Download

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-switch-rotation.jpg

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         https://blog.daitra.xyz/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.

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Jacob Dubail

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

Cool effect, though!

Martin Angelov

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.

Alex Forey

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

True that!

The lighting on the knob shouldn't rotate.

Martin Angelov

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

J.F. Herrera

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

cool work

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

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

Martin Angelov

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

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

damienfaivre.fr

really useful for me ! Thanks for this tutorial :)

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

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

Nathan B

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

Martin Angelov

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.

Nathan B

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?

e11world

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

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

Martin Angelov

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); 

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

Dan Netherton

Love it. Sleek and smooth.

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.

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

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.

How to use the knob to control system volume?

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

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

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

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.

Thks so much ! it's very helpful

matt garner

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

Ahmad Awais

Awesome code.

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

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

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

Magister

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.

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

Gabriel Malaquias

Very Cool! I will definitely use on future projects!

Hey,

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

Saif

Jo,
I tried the version with maxVal and minVal from

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

but there the knob spins around and looses the nice handling. Do you develop a newer version of this plugin?

Thanks, Stevoh

Rachel Reveley

It's beautiful but I couldn't get it to work on Android Chrome which is a shame.