6

Is there any way to add angular gradient to jQuery knob plugin, so that it starts from one color and along the arc, changes into another?

Adee
  • 464
  • 6
  • 17

2 Answers2

6

I digged the internet for a solution but no one ever tried this or posted a solution. Finally I am posting a Q&A. If anyone has a better solution, please share with us.

During the initialization, I have overridden the draw method and checked for the attribute shaded="true". If it's there, then a gradient is formed, starting from white and moving towards fgColor. To chose starting color other than white, set the attribute shadeColor="#(color hex code)".

<input class="knob" value="95" autocomplete="off" data-readOnly=true data-fgColor="#FF0000" data-bgColor="transparent" shaded="1" shadeColor="#00FF00"/>
<script>
    $(function(){
        $('.knob').knob({
            draw : function () {
                var a = this.angle(this.cv)  // Angle
                , sa = this.startAngle          // Previous start angle
                , sat = this.startAngle         // Start angle
                , ea                            // Previous end angle
                , eat = sat + a                 // End angle
                , r = 1;

                this.g.lineWidth = this.lineWidth;

                if(this.$.attr('shaded')){
                    var color1 = r ? this.o.fgColor : this.fgColor;
                    var color2 = this.$.attr('shadeColor') ? this.$.attr('shadeColor') : '#ffffff';
                    var grad = getGradient(color2, color1);

                    var saDeg = parseInt((sa * 180 / Math.PI) % 360);
                    var eatDeg = parseInt((eat * 180 / Math.PI) % 360);

                    for(var angle = saDeg;(angle % 360) != eatDeg;angle++){
                        sat = angle * (Math.PI / 180);
                        eat = (angle + 2) * (Math.PI / 180);

                        if(grad.color2[0] != grad.color1[0] && (angle + 1) % grad.steps[0] == 0){
                            grad.color1[0] += grad.adder[0];
                        }
                        if(grad.color2[1] != grad.color1[1] && (angle + 1) % grad.steps[1] == 0){
                            grad.color1[1] += grad.adder[1];
                        }
                        if(grad.color2[2] != grad.color1[2] && (angle + 1) % grad.steps[2] == 0){
                            grad.color1[2] += grad.adder[2];
                        }

                        color = '#' + toHex(grad.color1[0]) + toHex(grad.color1[1]) + toHex(grad.color1[2]);

                        this.g.beginPath();
                        this.g.strokeStyle = color;
                        this.g.arc(this.xy, this.xy, this.radius, sat, eat, false);
                        this.g.stroke();
                    }
                } else {
                    this.g.beginPath();
                    this.g.strokeStyle = r ? this.o.fgColor : this.fgColor ;
                    this.g.arc(this.xy, this.xy, this.radius, sat, eat, false);
                    this.g.stroke();
                }

                return false;
            }
        });
    });

    function getGradient(color1, color2){
        var ret = new Object();

        ret.color1 = new Array();
        ret.color2 = new Array();
        ret.steps = new Array();
        ret.adder = new Array();

        color1 = color1.replace('#','');
        ret.color1[0] = parseInt(color1.slice(0,2), 16),
        ret.color1[1] = parseInt(color1.slice(2,4), 16),
        ret.color1[2] = parseInt(color1.slice(4,6), 16);

        color2 = color2.replace('#','');
        ret.color2[0] = parseInt(color2.slice(0,2), 16),
        ret.color2[1] = parseInt(color2.slice(2,4), 16),
        ret.color2[2] = parseInt(color2.slice(4,6), 16);

        ret.steps[0] = (ret.color1[0] == ret.color2[0])? 0 : parseInt(360 / Math.abs(ret.color1[0] - ret.color2[0])),
        ret.steps[1] = (ret.color1[1] == ret.color2[1])? 0 : parseInt(360 / Math.abs(ret.color1[1] - ret.color2[1])),
        ret.steps[2] = (ret.color1[2] == ret.color2[2])? 0 : parseInt(360 / Math.abs(ret.color1[2] - ret.color2[2])),

        ret.adder[0] = (ret.color1[0] > ret.color2[0])? -1 : 1;
        ret.adder[1] = (ret.color1[1] > ret.color2[1])? -1 : 1;
        ret.adder[2] = (ret.color1[2] > ret.color2[2])? -1 : 1;

        return ret;
    }

    function toHex(number){
        number = number.toString(16);
        if(number.length < 2){
            number = '0' + number;
        }
        return number;
    }
</script>

It draws a separate arc for each degree (with arc angle of 2 degrees instead of one for the sake of smoothness). The colors of arcs go through a transition from fgColor to shadeColor.

The color mixing effect is like paint mixing rather than light mixing, so if you start from green and go towards red, you won't get the yellow shade in the center. It would look cool with light mixing effect but don't know how to do that exactly. Also it is not a well optimized code, it's just a solution. Huge room for improvement..

Adee
  • 464
  • 6
  • 17
  • Your circle doesn't begin at the top but 90 degrees east. Strange thing is that the red color stops at 90 degrees than the green color pops up... I tried to change the angle -90 degrees but the red color still stops at 90 degrees east. How can I change the red color to 0 degrees? Greetz Frank – Frank Oct 11 '13 at 06:43
  • @Frank I fixed it. Now it will start from 0. You can specify a starting angle other than 0 by using `data-angleOffset` parameter. I hope it will fill your requirement. – Adee Nov 07 '13 at 17:46
  • Thanks Adee for your response what was the problem? – Frank Dec 04 '13 at 10:47
  • You're welcome. I had hardcoded the starting angle to 90 degrees as in my case it started with an offset of 90 degrees. – Adee Dec 17 '13 at 11:33
2

Great, exactly what I needed! I added the "cursor" option as well, because that was the only thing that was not working yet.

var drawGradient = function () {

    var a = this.angle(this.cv)  // Angle
        , sa = this.startAngle          // Previous start angle
        , sat = this.startAngle         // Start angle
        , eat = sat + a                 // End angle
        , r = 1;

    this.g.lineCap = this.lineCap;
    this.g.lineWidth = this.lineWidth;

    if (this.o.bgColor !== "none") {
        this.g.beginPath();
        this.g.strokeStyle = this.o.bgColor;
        this.g.arc(this.xy, this.xy, this.radius, this.endAngle - 0.00001, this.startAngle + 0.00001, true);
        this.g.stroke();
    }

    if (this.$.attr('shaded')) {
        var color1 = r ? this.o.fgColor : this.fgColor;
        var color2 = this.$.attr('shadeColor') ? this.$.attr('shadeColor') : '#ffffff';
        var grad = getGradient(color2, color1);

        var saDeg = parseInt((sa * 180 / Math.PI) % 360);
        var eatDeg = parseInt((eat * 180 / Math.PI));

        var normalizedAngle = 0
        var normalizedEatAngle = eatDeg - saDeg;

        if(this.o.cursor == true) {
            var size = 40;
        } else if(this.o.cursor != false) {
            var size = this.o.cursor;
            this.o.cursor = true;
        }

        if(this.o.cursor) {
            if(normalizedEatAngle <= size) {
                normalizedEatAngle = size;
            }
        }
        for (var angle = saDeg; normalizedAngle < normalizedEatAngle; angle++, normalizedAngle++) {

            sat = angle * (Math.PI / 180);
            eat = (angle + 2) * (Math.PI / 180);

            if (grad.color2[0] != grad.color1[0] && (angle + 1) % grad.steps[0] == 0) {
                grad.color1[0] += grad.adder[0];
            }
            if (grad.color2[1] != grad.color1[1] && (angle + 1) % grad.steps[1] == 0) {
                grad.color1[1] += grad.adder[1];
            }
            if (grad.color2[2] != grad.color1[2] && (angle + 1) % grad.steps[2] == 0) {
                grad.color1[2] += grad.adder[2];
            }


            if(!this.o.cursor || (normalizedAngle + size) > normalizedEatAngle) {
                color = '#' + toHex(grad.color1[0]) + toHex(grad.color1[1]) + toHex(grad.color1[2]);
                this.g.beginPath();
                this.g.strokeStyle = color;
                this.g.arc(this.xy, this.xy, this.radius, sat, eat, false);
                this.g.stroke();
            }
        }
    } else {
        this.g.beginPath();
        this.g.strokeStyle = r ? this.o.fgColor : this.fgColor;
        this.g.arc(this.xy, this.xy, this.radius, sat, eat, false);
        this.g.stroke();
    }

    return false;
};
roebenk
  • 36
  • 7
elbowz
  • 557
  • 1
  • 4
  • 19